@jini-ai/agentic 0.3.7 → 0.4.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +33 -6
  3. package/dist/core/a2ui/agent-to-renderer.d.ts +3 -1
  4. package/dist/core/a2ui/agent-to-renderer.js +1 -1
  5. package/dist/core/a2ui/catalog.d.ts +14 -5
  6. package/dist/core/a2ui/catalog.js +5 -5
  7. package/dist/core/a2ui/common-types.d.ts +11 -3
  8. package/dist/core/a2ui/common-types.js +5 -3
  9. package/dist/core/a2ui/index.d.ts +1 -1
  10. package/dist/core/a2ui/index.js +1 -1
  11. package/dist/core/a2ui/interpreter.d.ts +45 -10
  12. package/dist/core/a2ui/interpreter.js +38 -43
  13. package/dist/core/a2ui/json-pointer.d.ts +16 -4
  14. package/dist/core/a2ui/json-pointer.js +7 -7
  15. package/dist/core/a2ui/renderer-to-agent.d.ts +22 -8
  16. package/dist/core/a2ui/renderer-to-agent.js +5 -5
  17. package/dist/core/a2ui/resolve.d.ts +7 -2
  18. package/dist/core/a2ui/resolve.js +10 -9
  19. package/dist/core/a2ui/tree.d.ts +8 -2
  20. package/dist/core/a2ui/tree.js +2 -2
  21. package/dist/core/ag-ui.d.ts +17 -9
  22. package/dist/core/ag-ui.js +4 -4
  23. package/dist/core/capability.d.ts +12 -3
  24. package/dist/core/capability.js +3 -3
  25. package/dist/core/dom/dom-page-driver.d.ts +8 -4
  26. package/dist/core/dom/dom-page-driver.js +19 -20
  27. package/dist/core/dom/index.d.ts +3 -3
  28. package/dist/core/dom/index.js +1 -1
  29. package/dist/core/dom/model-context.d.ts +18 -8
  30. package/dist/core/dom/model-context.js +12 -15
  31. package/dist/core/dom/native-model-context.d.ts +6 -0
  32. package/dist/core/dom/native-model-context.js +19 -0
  33. package/dist/core/element-handles.d.ts +6 -2
  34. package/dist/core/element-handles.js +3 -3
  35. package/dist/core/gen-ui/encoder.d.ts +21 -11
  36. package/dist/core/gen-ui/encoder.js +8 -7
  37. package/dist/core/gen-ui/events.d.ts +6 -5
  38. package/dist/core/gen-ui/events.js +6 -5
  39. package/dist/core/gen-ui/index.d.ts +1 -1
  40. package/dist/core/gen-ui/index.js +1 -1
  41. package/dist/core/guards.d.ts +17 -5
  42. package/dist/core/guards.js +6 -6
  43. package/dist/core/handle.d.ts +12 -5
  44. package/dist/core/handle.js +9 -8
  45. package/dist/core/index.d.ts +2 -1
  46. package/dist/core/index.js +2 -1
  47. package/dist/core/list-handles.d.ts +4 -1
  48. package/dist/core/list-handles.js +1 -1
  49. package/dist/core/mcp-ui-apps.d.ts +39 -9
  50. package/dist/core/mcp-ui-apps.js +10 -8
  51. package/dist/core/messages.d.ts +6 -0
  52. package/dist/core/messages.js +6 -0
  53. package/dist/core/page-capabilities.d.ts +0 -11
  54. package/dist/core/page-capabilities.js +13 -1
  55. package/dist/core/page-driver.d.ts +32 -11
  56. package/dist/core/page-executor.d.ts +8 -2
  57. package/dist/core/page-executor.js +31 -31
  58. package/dist/core/webmcp.d.ts +27 -8
  59. package/dist/core/webmcp.js +14 -14
  60. package/dist/index.d.ts +30 -0
  61. package/dist/index.js +30 -0
  62. package/dist/skills/install/archive.d.ts +8 -0
  63. package/dist/skills/install/archive.js +47 -0
  64. package/dist/skills/install/fetch-adapter.d.ts +6 -0
  65. package/dist/skills/install/fetch-adapter.js +6 -0
  66. package/dist/skills/install/github.d.ts +21 -0
  67. package/dist/skills/install/github.js +92 -0
  68. package/dist/skills/install/index.d.ts +11 -0
  69. package/dist/skills/install/index.js +11 -0
  70. package/dist/skills/install/install-service.d.ts +44 -0
  71. package/dist/skills/install/install-service.js +121 -0
  72. package/dist/skills/install/layout.d.ts +13 -0
  73. package/dist/skills/install/layout.js +22 -0
  74. package/dist/skills/install/live-registration.d.ts +61 -0
  75. package/dist/skills/install/live-registration.js +64 -0
  76. package/dist/skills/install/node-filesystem.d.ts +4 -0
  77. package/dist/skills/install/node-filesystem.js +41 -0
  78. package/dist/skills/install/ports.d.ts +101 -0
  79. package/dist/skills/install/ports.js +2 -0
  80. package/dist/skills/install/state.d.ts +13 -0
  81. package/dist/skills/install/state.js +23 -0
  82. package/dist/skills/install/validation.d.ts +45 -0
  83. package/dist/skills/install/validation.js +104 -0
  84. package/package.json +20 -5
@@ -17,7 +17,7 @@
17
17
  * @param id - Capability id to resolve.
18
18
  * @returns The definition, or `undefined` when the id is not in the manifest.
19
19
  */
20
- export function findCapability(capabilities, id) {
20
+ export function findCapability({ capabilities, id }, _optional = {}) {
21
21
  return capabilities.find((capability) => capability.id === id);
22
22
  }
23
23
  /**
@@ -33,7 +33,7 @@ export function findCapability(capabilities, id) {
33
33
  * @param input - Raw caller-supplied arguments.
34
34
  * @returns An error message, or `null` when the input is acceptable.
35
35
  */
36
- export function findCapabilityInputError(capability, input) {
36
+ export function findCapabilityInputError({ capability, input }, _optional = {}) {
37
37
  const { properties, required, additionalProperties } = capability.inputSchema;
38
38
  for (const name of required ?? []) {
39
39
  if (input[name] === undefined)
@@ -73,7 +73,7 @@ export function findCapabilityInputError(capability, input) {
73
73
  * @param hasSession - Whether a live frontend session is attached.
74
74
  * @returns Only the capabilities that can actually be satisfied right now.
75
75
  */
76
- export function availableCapabilities(capabilities, hasSession) {
76
+ export function availableCapabilities({ capabilities, hasSession }, _optional = {}) {
77
77
  return hasSession ? capabilities : capabilities.filter((c) => c.surface === 'server');
78
78
  }
79
79
  //# sourceMappingURL=capability.js.map
@@ -4,15 +4,17 @@ export interface DomPageDriverPage {
4
4
  /** Shown to a caller by `page.find_elements`/`page.navigate`'s refusal message — see
5
5
  * `PageSummary.label` for why this exists at all. */
6
6
  readonly label: string;
7
- readonly navigate: () => void;
7
+ readonly navigate: (required: Record<string, never>) => void;
8
8
  }
9
- export interface DomPageDriverOptions {
9
+ export interface DomPageDriverRequired {
10
10
  /** The subtree to expose. Never pass `document`. */
11
11
  readonly root: ParentNode & {
12
12
  querySelector: Element['querySelector'];
13
13
  };
14
14
  /** Page ids this driver may navigate to, each with its label and how to get there. */
15
15
  readonly pages: Readonly<Record<string, DomPageDriverPage>>;
16
+ }
17
+ export interface DomPageDriverOptions {
16
18
  /**
17
19
  * The id of the page currently shown, reported on every element.
18
20
  *
@@ -30,7 +32,9 @@ export interface DomPageDriverOptions {
30
32
  * @param options - The subtree, the navigable pages, and the current page id.
31
33
  * @returns A driver ready to hand to `executePageCapability`.
32
34
  */
33
- export declare function createDomPageDriver(options: DomPageDriverOptions): PageDriver;
35
+ export declare function createDomPageDriver({ root, pages }: DomPageDriverRequired, options?: DomPageDriverOptions): PageDriver;
34
36
  /** Reads the current page's `data-agent-page`, for hosts that tag the body. */
35
- export declare function currentAgentPage(root: ParentNode): string | undefined;
37
+ export declare function currentAgentPage({ root }: {
38
+ root: ParentNode;
39
+ }, _optional?: Record<string, never>): string | undefined;
36
40
  //# sourceMappingURL=dom-page-driver.d.ts.map
@@ -228,8 +228,7 @@ function describe(element, page) {
228
228
  * @param options - The subtree, the navigable pages, and the current page id.
229
229
  * @returns A driver ready to hand to `executePageCapability`.
230
230
  */
231
- export function createDomPageDriver(options) {
232
- const { root, pages } = options;
231
+ export function createDomPageDriver({ root, pages }, options = {}) {
233
232
  /**
234
233
  * The page a specific element belongs to — its own nearest `[data-agent-page]` ancestor, not a
235
234
  * single document-wide guess.
@@ -257,7 +256,7 @@ export function createDomPageDriver(options) {
257
256
  * publishes the same handle twice makes `find`'s single-match resolution ambiguous — surfaced
258
257
  * here so both callers below can refuse rather than silently pick one.
259
258
  */
260
- const findAll = (handle) => Array.from(root.querySelectorAll(resolveHandleSelector(handle)))
259
+ const findAll = (handle) => Array.from(root.querySelectorAll(resolveHandleSelector({ handle })))
261
260
  .filter((element) => !isPrivate(element));
262
261
  /**
263
262
  * More than one element answers to the same handle — a page-authoring bug (duplicate
@@ -312,7 +311,7 @@ export function createDomPageDriver(options) {
312
311
  return candidate ?? element;
313
312
  };
314
313
  return {
315
- async findElements(filter) {
314
+ async findElements(_required, filter = {}) {
316
315
  const found = Array.from(root.querySelectorAll(`[${AGENT_ELEMENT_ATTRIBUTE}]`))
317
316
  .filter((element) => !isPrivate(element))
318
317
  .map((element) => describe(element, pageOf(element)))
@@ -327,16 +326,16 @@ export function createDomPageDriver(options) {
327
326
  || element.label.toLowerCase().includes(query);
328
327
  });
329
328
  },
330
- async listPages() {
329
+ async listPages(_required, _optional = {}) {
331
330
  // `Object.entries`, same own-enumerable-keys-only guarantee `Object.keys` gives `navigate`'s
332
331
  // own `hasOwnProperty` check above — no risk of a `pages.label`/`pages.navigate`-shaped
333
332
  // prototype entry leaking in as a fake page id.
334
333
  return Object.entries(pages).map(([id, page]) => ({ id, label: page.label }));
335
334
  },
336
- async describeField(handle) {
335
+ async describeField({ handle }, _optional = {}) {
337
336
  return fieldDescriptorOf(controlOf(find(handle)));
338
337
  },
339
- async describeState(handle) {
338
+ async describeState({ handle }, _optional = {}) {
340
339
  // Unlike every other method here, a handle that no longer resolves is an answer rather than
341
340
  // an error: an element that a click removed is exactly what the caller is asking about. A
342
341
  // handle resolving to *more than one* element is a different situation entirely — not an
@@ -402,7 +401,7 @@ export function createDomPageDriver(options) {
402
401
  * would hang for the whole life of a backgrounded tab; without the frames every write would
403
402
  * report its own target unchanged, because the read would beat the render.
404
403
  */
405
- async settle() {
404
+ async settle(_required, _optional = {}) {
406
405
  await new Promise((resolve) => {
407
406
  let timer;
408
407
  // No re-entry guard: whichever side loses the race calls this again, and both of its
@@ -422,7 +421,7 @@ export function createDomPageDriver(options) {
422
421
  raf(() => raf(finish));
423
422
  });
424
423
  },
425
- async highlight(handle, durationMs) {
424
+ async highlight({ handle, durationMs }, _optional = {}) {
426
425
  const element = find(handle);
427
426
  // Same refusal as `click()`, for the same reason: an SVG (or other non-HTMLElement) target
428
427
  // has no `.style` to draw an outline on, so silently returning here drew nothing while
@@ -448,16 +447,16 @@ export function createDomPageDriver(options) {
448
447
  }, durationMs);
449
448
  highlights.set(handle, { timer, restore });
450
449
  },
451
- async scrollTo(handle) {
450
+ async scrollTo({ handle }, _optional = {}) {
452
451
  find(handle).scrollIntoView({ behavior: 'smooth', block: 'center' });
453
452
  },
454
- async click(handle) {
453
+ async click({ handle }, _optional = {}) {
455
454
  const control = controlOf(find(handle));
456
455
  if (!(control instanceof HTMLElement))
457
456
  throw new Error(`"${handle}" is not clickable`);
458
457
  control.click();
459
458
  },
460
- async fill(handle, text) {
459
+ async fill({ handle, text }, _optional = {}) {
461
460
  const control = controlOf(find(handle));
462
461
  if (isEditableRegion(control)) {
463
462
  // A contenteditable region has no `value`; its content *is* its children, and a real
@@ -483,7 +482,7 @@ export function createDomPageDriver(options) {
483
482
  control.dispatchEvent(new Event('input', { bubbles: true }));
484
483
  control.dispatchEvent(new Event('change', { bubbles: true }));
485
484
  },
486
- async selectOption(handle, option, selected = true) {
485
+ async selectOption({ handle, option }, { selected = true } = {}) {
487
486
  const control = controlOf(find(handle));
488
487
  if (!(control instanceof HTMLSelectElement)) {
489
488
  throw new Error(`"${handle}" is not a dropdown`);
@@ -518,8 +517,8 @@ export function createDomPageDriver(options) {
518
517
  // the same shape of problem regardless of which side authored it. `handle` needs no
519
518
  // treatment: it is already validated ASCII, length-capped by isValidElementHandle before
520
519
  // this driver is ever called.
521
- const safeOption = normalizeAgentLabel(option).text;
522
- const available = options.map((entry) => normalizeAgentLabel(entry.text.trim()).text)
520
+ const safeOption = normalizeAgentLabel({ raw: option }).text;
521
+ const available = options.map((entry) => normalizeAgentLabel({ raw: entry.text.trim() }).text)
523
522
  .filter((text) => text.length > 0);
524
523
  throw new Error(`"${safeOption}" is not an option of "${handle}". Available: ${available.length > 0 ? available.join(', ') : '(none)'}`);
525
524
  }
@@ -535,7 +534,7 @@ export function createDomPageDriver(options) {
535
534
  // ignoring this would tell the caller a deselect happened when nothing changed, and clearing
536
535
  // the control would invent a third state (no selection) that a native <select> cannot hold.
537
536
  throw new Error(`"${handle}" is a single-select; its choice cannot be removed, only replaced — `
538
- + `select a different option instead of deselecting "${normalizeAgentLabel(option).text}"`);
537
+ + `select a different option instead of deselecting "${normalizeAgentLabel({ raw: option }).text}"`);
539
538
  }
540
539
  else {
541
540
  // Same prototype-setter dance as `fill`, for the same reason: React tracks the previous
@@ -549,7 +548,7 @@ export function createDomPageDriver(options) {
549
548
  control.dispatchEvent(new Event('input', { bubbles: true }));
550
549
  control.dispatchEvent(new Event('change', { bubbles: true }));
551
550
  },
552
- async navigate(page) {
551
+ async navigate({ page }, _optional = {}) {
553
552
  // `hasOwnProperty`, not `pages[page]`: a plain object literal inherits everything
554
553
  // `Object.prototype` contributes, so a bare lookup answered `navigate("constructor")` with
555
554
  // the `Object` constructor — a function the host never published, which this then called,
@@ -564,8 +563,8 @@ export function createDomPageDriver(options) {
564
563
  // the executor entirely, hands `page` in unsanitized, so the same bound-and-strip treatment
565
564
  // applies here too rather than assuming the only caller is the one that already checked.
566
565
  if (!published)
567
- throw new Error(`"${normalizeAgentLabel(page).text}" is not a published page`);
568
- pages[page].navigate();
566
+ throw new Error(`"${normalizeAgentLabel({ raw: page }).text}" is not a published page`);
567
+ pages[page].navigate({});
569
568
  },
570
569
  };
571
570
  }
@@ -616,7 +615,7 @@ function fillEditableRegion(control, text) {
616
615
  control.dispatchEvent(new Event('input', { bubbles: true }));
617
616
  }
618
617
  /** Reads the current page's `data-agent-page`, for hosts that tag the body. */
619
- export function currentAgentPage(root) {
618
+ export function currentAgentPage({ root }, _optional = {}) {
620
619
  const tagged = root.querySelector(`[${AGENT_PAGE_ATTRIBUTE}]`);
621
620
  return tagged?.getAttribute(AGENT_PAGE_ATTRIBUTE) ?? undefined;
622
621
  }
@@ -3,15 +3,15 @@
3
3
  *
4
4
  * The one `PageDriver` (see `@jini-ai/agentic`'s `page-driver.ts`) that reads and writes a real DOM
5
5
  * subtree. Split into its own entry point, compiled under its own `DOM`-lib `tsconfig.dom.json`,
6
- * so the rest of this package can stay provably DOM-free — see source-map.md's "The DOM split".
6
+ * so the rest of this package can stay provably DOM-free — see archived provenance ledger's "The DOM split".
7
7
  *
8
8
  * `@jini-ai/chat-react` is this package's one in-repo consumer today.
9
9
  */
10
- export { createDomPageDriver, currentAgentPage, type DomPageDriverOptions, type DomPageDriverPage, } from './dom-page-driver.js';
10
+ export { createDomPageDriver, currentAgentPage, type DomPageDriverRequired, type DomPageDriverOptions, type DomPageDriverPage, } from './dom-page-driver.js';
11
11
  /**
12
12
  * WebMCP feature detection — moved 2026-07-26 from `@jini-ai/ui`'s `agent-tools/model-context.ts`
13
13
  * (plan §8 step 6). See `model-context.ts`'s module doc for why it lives here rather than
14
14
  * alongside `../webmcp.ts`'s DOM-free projection.
15
15
  */
16
- export { getAgentModelContext, type AgentModelContextLike, type AgentModelContextToolRegistration, } from './model-context.js';
16
+ export { getAgentModelContext, type AgentModelContextLike, type ModelContextHostPort, type AgentModelContextRegisterToolOptions, type AgentModelContextToolRegistration, } from './model-context.js';
17
17
  //# sourceMappingURL=index.d.ts.map
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * The one `PageDriver` (see `@jini-ai/agentic`'s `page-driver.ts`) that reads and writes a real DOM
5
5
  * subtree. Split into its own entry point, compiled under its own `DOM`-lib `tsconfig.dom.json`,
6
- * so the rest of this package can stay provably DOM-free — see source-map.md's "The DOM split".
6
+ * so the rest of this package can stay provably DOM-free — see archived provenance ledger's "The DOM split".
7
7
  *
8
8
  * `@jini-ai/chat-react` is this package's one in-repo consumer today.
9
9
  */
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * @module dom/model-context
3
3
  *
4
- * Feature-detected access to the browser's (draft) WebMCP surface —
5
- * `document.modelContext`, falling back to the deprecated `navigator.modelContext`
4
+ * Host-injected access to the browser's (draft) WebMCP surface —
5
+ * the host supplies `document.modelContext`, followed by the deprecated `navigator.modelContext`
6
6
  * alias (the spec moved the getter from Navigator to Document in 2026;
7
7
  * see the polyfill ecosystem's own back-compat posture). No `@mcp-b/*` or
8
8
  * other WebMCP package dependency: this module only needs the shape, the
@@ -11,11 +11,11 @@
11
11
  * bridges already use.
12
12
  * A page that wants `document.modelContext` to actually exist in a browser
13
13
  * without native support must load a real polyfill itself (e.g.
14
- * `@mcp-b/webmcp-polyfill`) — this module only ever reads what's there.
14
+ * `@mcp-b/webmcp-polyfill`) — this module adapts only the candidates the host supplies.
15
15
  *
16
16
  * Moved 2026-07-26 from `@jini-ai/ui/src/features/agent-tools/model-context.ts` (plan §4/§8 step 6).
17
17
  * Lives under `src/dom/`, not alongside `../webmcp.ts`'s pure `CapabilityDef` → WebMCP-tool
18
- * projection, because it reads `document`/`navigator` — `webmcp.ts`'s own module doc is explicit
18
+ * projection, because it adapts the native browser API — `webmcp.ts`'s own module doc is explicit
19
19
  * that "that detection is deliberately NOT here: it touches browser globals, and this package
20
20
  * holds none," which is exactly the DOM-free guarantee `src/dom/` exists to carve an exception
21
21
  * for. See `dom/index.ts`'s re-export of this module.
@@ -56,14 +56,24 @@ export interface AgentModelContextLike {
56
56
  * ModelContextRegisterToolOptions options = {})` — it is **async**. Callers must not assume it
57
57
  * has taken effect synchronously, and must not let a rejection escape.
58
58
  */
59
- registerTool(tool: AgentModelContextToolRegistration, options?: AgentModelContextRegisterToolOptions): Promise<void>;
59
+ registerTool(required: {
60
+ tool: AgentModelContextToolRegistration;
61
+ }, options?: AgentModelContextRegisterToolOptions): Promise<void>;
60
62
  /**
61
63
  * NOT in the spec — the draft's only unregistration mechanism is aborting the `signal` passed to
62
64
  * `registerTool`. Kept optional purely so a polyfill that happens to expose one can be used;
63
65
  * never rely on its presence, and always pass a `signal` as the real cleanup path.
64
66
  */
65
- unregisterTool?(name: string): void;
67
+ unregisterTool?(required: {
68
+ name: string;
69
+ }, _optional?: Record<string, never>): void;
66
70
  }
67
- /** Returns the page's WebMCP surface if one is installed (native or polyfilled), or `undefined`. */
68
- export declare function getAgentModelContext(): AgentModelContextLike | undefined;
71
+ /** Ordered host candidates: modern document first, then any deprecated navigator alias. */
72
+ export interface ModelContextHostPort {
73
+ candidates(required: Record<string, never>): readonly unknown[];
74
+ }
75
+ /** Adapts the first installed native surface to Jini's argument-object port. */
76
+ export declare function getAgentModelContext({ host }: {
77
+ host: ModelContextHostPort;
78
+ }, _optional?: Record<string, never>): AgentModelContextLike | undefined;
69
79
  //# sourceMappingURL=model-context.d.ts.map
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * @module dom/model-context
3
3
  *
4
- * Feature-detected access to the browser's (draft) WebMCP surface —
5
- * `document.modelContext`, falling back to the deprecated `navigator.modelContext`
4
+ * Host-injected access to the browser's (draft) WebMCP surface —
5
+ * the host supplies `document.modelContext`, followed by the deprecated `navigator.modelContext`
6
6
  * alias (the spec moved the getter from Navigator to Document in 2026;
7
7
  * see the polyfill ecosystem's own back-compat posture). No `@mcp-b/*` or
8
8
  * other WebMCP package dependency: this module only needs the shape, the
@@ -11,11 +11,11 @@
11
11
  * bridges already use.
12
12
  * A page that wants `document.modelContext` to actually exist in a browser
13
13
  * without native support must load a real polyfill itself (e.g.
14
- * `@mcp-b/webmcp-polyfill`) — this module only ever reads what's there.
14
+ * `@mcp-b/webmcp-polyfill`) — this module adapts only the candidates the host supplies.
15
15
  *
16
16
  * Moved 2026-07-26 from `@jini-ai/ui/src/features/agent-tools/model-context.ts` (plan §4/§8 step 6).
17
17
  * Lives under `src/dom/`, not alongside `../webmcp.ts`'s pure `CapabilityDef` → WebMCP-tool
18
- * projection, because it reads `document`/`navigator` — `webmcp.ts`'s own module doc is explicit
18
+ * projection, because it adapts the native browser API — `webmcp.ts`'s own module doc is explicit
19
19
  * that "that detection is deliberately NOT here: it touches browser globals, and this package
20
20
  * holds none," which is exactly the DOM-free guarantee `src/dom/` exists to carve an exception
21
21
  * for. See `dom/index.ts`'s re-export of this module.
@@ -28,17 +28,14 @@
28
28
  * fixture: a raw (non-`toWebMcpTool`) registration carrying a `title` failed TypeScript's
29
29
  * excess-property check against the un-widened type here.
30
30
  */
31
- function isModelContext(value) {
32
- return typeof value === 'object' && value !== null && typeof value.registerTool === 'function';
33
- }
34
- /** Returns the page's WebMCP surface if one is installed (native or polyfilled), or `undefined`. */
35
- export function getAgentModelContext() {
36
- const doc = globalThis.document;
37
- if (isModelContext(doc?.modelContext))
38
- return doc.modelContext;
39
- const nav = globalThis.navigator;
40
- if (isModelContext(nav?.modelContext))
41
- return nav.modelContext;
31
+ import { adaptNativeModelContext } from './native-model-context.js';
32
+ /** Adapts the first installed native surface to Jini's argument-object port. */
33
+ export function getAgentModelContext({ host }, _optional = {}) {
34
+ for (const value of host.candidates({})) {
35
+ const context = adaptNativeModelContext({ value });
36
+ if (context)
37
+ return context;
38
+ }
42
39
  return undefined;
43
40
  }
44
41
  //# sourceMappingURL=model-context.js.map
@@ -0,0 +1,6 @@
1
+ import type { AgentModelContextLike } from './model-context.js';
2
+ /** Preserve native receiver binding and stable port identity across feature detection. */
3
+ export declare function adaptNativeModelContext({ value }: {
4
+ value: unknown;
5
+ }): AgentModelContextLike | undefined;
6
+ //# sourceMappingURL=native-model-context.d.ts.map
@@ -0,0 +1,19 @@
1
+ const adapters = new WeakMap();
2
+ /** Preserve native receiver binding and stable port identity across feature detection. */
3
+ export function adaptNativeModelContext({ value }) {
4
+ if (typeof value !== 'object' || value === null || typeof value.registerTool !== 'function')
5
+ return undefined;
6
+ const existing = adapters.get(value);
7
+ if (existing)
8
+ return existing;
9
+ const native = value;
10
+ const adapter = {
11
+ registerTool: ({ tool }, options) => native.registerTool(tool, options),
12
+ ...(typeof native.unregisterTool === 'function'
13
+ ? { unregisterTool: ({ name }) => native.unregisterTool(name) }
14
+ : {}),
15
+ };
16
+ adapters.set(value, adapter);
17
+ return adapter;
18
+ }
19
+ //# sourceMappingURL=native-model-context.js.map
@@ -28,7 +28,9 @@ export declare const AGENT_ELEMENT_ROLES: readonly AgentElementRole[];
28
28
  * @param handle - Candidate handle, typically caller-supplied.
29
29
  * @returns True when it is safe to interpolate into an attribute selector.
30
30
  */
31
- export declare function isValidElementHandle(handle: string): boolean;
31
+ export declare function isValidElementHandle({ handle }: {
32
+ handle: string;
33
+ }, _optional?: Record<string, never>): boolean;
32
34
  /**
33
35
  * Builds the one selector shape this system will ever resolve.
34
36
  *
@@ -36,7 +38,9 @@ export declare function isValidElementHandle(handle: string): boolean;
36
38
  * @returns The attribute selector for that handle.
37
39
  * @throws If the handle is not valid — never fall back to treating it as a raw selector.
38
40
  */
39
- export declare function resolveHandleSelector(handle: string): string;
41
+ export declare function resolveHandleSelector({ handle }: {
42
+ handle: string;
43
+ }, _optional?: Record<string, never>): string;
40
44
  /** A tagged element as described back to a caller. `label` is page-authored, so it is untrusted. */
41
45
  export interface AgentElementDescriptor {
42
46
  readonly handle: string;
@@ -25,7 +25,7 @@ const MAX_HANDLE_LENGTH = 128;
25
25
  * @param handle - Candidate handle, typically caller-supplied.
26
26
  * @returns True when it is safe to interpolate into an attribute selector.
27
27
  */
28
- export function isValidElementHandle(handle) {
28
+ export function isValidElementHandle({ handle }, _optional = {}) {
29
29
  return handle.length > 0 && handle.length <= MAX_HANDLE_LENGTH && HANDLE_PATTERN.test(handle);
30
30
  }
31
31
  /**
@@ -35,8 +35,8 @@ export function isValidElementHandle(handle) {
35
35
  * @returns The attribute selector for that handle.
36
36
  * @throws If the handle is not valid — never fall back to treating it as a raw selector.
37
37
  */
38
- export function resolveHandleSelector(handle) {
39
- if (!isValidElementHandle(handle)) {
38
+ export function resolveHandleSelector({ handle }, _optional = {}) {
39
+ if (!isValidElementHandle({ handle })) {
40
40
  throw new Error(`invalid element handle "${handle.slice(0, MAX_HANDLE_LENGTH)}": `
41
41
  + 'handles are lowercase words joined by single hyphens, and are never CSS selectors');
42
42
  }
@@ -1,18 +1,20 @@
1
+ import type { Clock } from "@jini-ai/core/primitives";
1
2
  /**
2
- * @module agui/encoder
3
+ * @module gen-ui/encoder
3
4
  *
4
- * Encodes `@jini-ai/protocol`'s `RunProtocolEvent` stream into AG-UI wire events (see
5
+ * Encodes `@jini-ai/protocol`'s `RunProtocolEvent` stream into custom gen-ui wire events (see
5
6
  * `./events.ts`'s module doc). Rewritten from scratch against `@jini-ai/protocol`'s actual
6
7
  * current shape — the ported adapter's original encoder targeted a different, product-specific
7
8
  * event union with no direct correspondence to `RunProtocolEvent`/`RunAgentPayload`; only the
8
9
  * *shape* of the job (a big per-event-kind switch, unrecognized events silently dropped) carried
9
- * over. See `source-map.md` for the full old→new field-mapping table and the generalization
10
+ * over. See `archived provenance ledger` for the full old→new field-mapping table and the generalization
10
11
  * writeup for the six event kinds that had no generic equivalent before this task.
11
12
  *
12
13
  * Folded in 2026-07-26 from the standalone `@jini-ai/agui` package (plan §3a) into this `src/gen-ui/`
13
14
  * subdirectory — kept out of the flat `ag-ui.ts` namespace because that file is a different
14
15
  * projection ({@link CapabilityDef} → an AG-UI *frontend tool*); this one turns a run's event
15
- * stream into AG-UI *wire* events. Same protocol, two unrelated halves of it.
16
+ * stream into Jini's own gen-ui *wire* events. These are separate protocols; the old package name
17
+ * did not make this custom event vocabulary compatible with external AG-UI.
16
18
  */
17
19
  import type { RunProtocolEvent } from '@jini-ai/protocol';
18
20
  import type { GenUiEvent } from './events.js';
@@ -20,12 +22,12 @@ export interface GenUiEncodeContext {
20
22
  /** The run this event belongs to — stamped onto every produced `GenUiEvent`. */
21
23
  readonly runId: string;
22
24
  /** Passed through onto the produced event's `seq`, when the caller tracks one (e.g. an SSE `Last-Event-ID`-shaped cursor). Omitted entirely (not set to `undefined`) when absent, matching the original adapter's own base-field construction. */
23
- readonly seq?: number;
24
- /** Injectable clock for the produced event's `ts`. Defaults to `Date.now`. Test-only hook. */
25
- readonly now?: () => number;
25
+ readonly seq?: number | undefined;
26
+ /** Optional clock override for this event; the factory's required clock supplies the default. */
27
+ readonly now?: ((required: Record<string, never>) => number) | undefined;
26
28
  }
27
29
  /**
28
- * A stateful AG-UI encoder for one run's event stream. Stateful because `tool_use`/`tool_result`
30
+ * A stateful gen-ui encoder for one run's event stream. Stateful because `tool_use`/`tool_result`
29
31
  * correlation requires remembering each in-flight tool call's name/args between the two protocol
30
32
  * events that describe its start and end — a pure per-event function cannot do this alone. Create
31
33
  * one instance per run (e.g. one per `RunLifecycle.stream(runId, ...)` subscription); do not share
@@ -34,11 +36,17 @@ export interface GenUiEncodeContext {
34
36
  */
35
37
  export interface GenUiEncoder {
36
38
  /**
37
- * Encodes one `RunProtocolEvent` into an `GenUiEvent`, or `null` if this event has no AG-UI
39
+ * Encodes one `RunProtocolEvent` into a `GenUiEvent`, or `null` if this event has no gen-ui
38
40
  * equivalent (an unrecognized/not-yet-generalized event kind — silently dropped by the relay,
39
41
  * matching the original adapter's own default behavior).
40
42
  */
41
- encode(event: RunProtocolEvent, ctx: GenUiEncodeContext): GenUiEvent | null;
43
+ encode(required: {
44
+ event: RunProtocolEvent;
45
+ runId: string;
46
+ }, optional?: {
47
+ seq?: number | undefined;
48
+ now?: ((required: Record<string, never>) => number) | undefined;
49
+ }): GenUiEvent | null;
42
50
  }
43
51
  /**
44
52
  * Creates a fresh `GenUiEncoder` with its own, empty tool-call correlation map.
@@ -46,5 +54,7 @@ export interface GenUiEncoder {
46
54
  * of currently in-flight (not yet resolved) tool calls for the run this instance encodes.
47
55
  * @overallScore 100/100
48
56
  */
49
- export declare function createGenUiEncoder(): GenUiEncoder;
57
+ export declare function createGenUiEncoder({ clock }: {
58
+ clock: Clock;
59
+ }, _optional?: Record<string, never>): GenUiEncoder;
50
60
  //# sourceMappingURL=encoder.d.ts.map
@@ -4,10 +4,10 @@
4
4
  * of currently in-flight (not yet resolved) tool calls for the run this instance encodes.
5
5
  * @overallScore 100/100
6
6
  */
7
- export function createGenUiEncoder() {
7
+ export function createGenUiEncoder({ clock }, _optional = {}) {
8
8
  const pendingToolCalls = new Map();
9
9
  function baseFields(ctx) {
10
- const ts = ctx.now ? ctx.now() : Date.now();
10
+ const ts = ctx.now ? ctx.now({}) : clock.nowMs();
11
11
  return ctx.seq !== undefined ? { runId: ctx.runId, ts, seq: ctx.seq } : { runId: ctx.runId, ts };
12
12
  }
13
13
  function encodeAgentPayload(payload, ctx) {
@@ -38,7 +38,7 @@ export function createGenUiEncoder() {
38
38
  result: payload.content,
39
39
  };
40
40
  }
41
- // Generalization (see source-map.md): pipeline_stage_started/completed generalized into a
41
+ // Generalization (see archived provenance ledger): pipeline_stage_started/completed generalized into a
42
42
  // named-stage boundary any multi-step driver can emit, flowing through the same 'agent'
43
43
  // channel as tool_use/tool_result.
44
44
  case 'stage_start':
@@ -57,19 +57,20 @@ export function createGenUiEncoder() {
57
57
  stageId: payload.stageId,
58
58
  ...(payload.iteration !== undefined ? { iteration: payload.iteration } : {}),
59
59
  };
60
- // Generalization (see source-map.md): genui_surface_request/response generalized into a
60
+ // Generalization (see archived provenance ledger): genui_surface_request/response generalized into a
61
61
  // generic human-in-the-loop ask/answer pair.
62
62
  case 'surface_request':
63
63
  return { ...base, kind: 'ui.surface_requested', surfaceId: payload.surfaceId, surfaceKind: payload.surfaceKind, payload: payload.payload };
64
64
  case 'surface_response':
65
65
  return { ...base, kind: 'ui.surface_responded', surfaceId: payload.surfaceId, value: payload.value, respondedBy: payload.respondedBy };
66
66
  // 'status' / 'thinking_start' / 'thinking_delta' / 'tool_input_delta' / 'usage' / 'raw':
67
- // no AG-UI equivalent — silently dropped, matching the original adapter's default behavior.
67
+ // no gen-ui equivalent — silently dropped, matching the original adapter's default behavior.
68
68
  default:
69
69
  return null;
70
70
  }
71
71
  }
72
- function encode(event, ctx) {
72
+ function encode({ event, runId }, { seq, now } = {}) {
73
+ const ctx = { runId, seq, now };
73
74
  switch (event.kind) {
74
75
  case 'start':
75
76
  return { ...baseFields(ctx), kind: 'run.lifecycle', status: 'started' };
@@ -86,7 +87,7 @@ export function createGenUiEncoder() {
86
87
  }
87
88
  case 'agent':
88
89
  return encodeAgentPayload(event.payload, ctx);
89
- // 'stdout' / 'stderr' / 'error': no AG-UI equivalent — silently dropped, matching the
90
+ // 'stdout' / 'stderr' / 'error': no gen-ui equivalent — silently dropped, matching the
90
91
  // original adapter's default behavior for unrecognized event kinds.
91
92
  default:
92
93
  return null;
@@ -1,11 +1,12 @@
1
1
  /**
2
- * @module agui/events
2
+ * @module gen-ui/events
3
3
  *
4
- * The AG-UI wire event shapes `./encoder.ts` produces. AG-UI (Agent-User Interaction
5
- * Protocol) is CopilotKit's open, external wire protocol for streaming an agent's run over SSE to
6
- * a UI client — see https://github.com/ag-ui-protocol/ag-ui. This module is a near-verbatim port
4
+ * The custom gen-ui wire event shapes `./encoder.ts` produces. AG-UI (Agent-User Interaction
5
+ * Protocol) is CopilotKit's separate, external wire protocol for streaming an agent's run over SSE to
6
+ * a UI client — see https://github.com/ag-ui-protocol/ag-ui. These six `kind` values belong to
7
+ * Jini's own protocol, not the external AG-UI event vocabulary. This module is a near-verbatim port
7
8
  * of a 312-line adapter that encoded a product's own run-event stream into this shape; the types
8
- * themselves are unchanged in kind, only de-branded (see `source-map.md` for the full
9
+ * themselves are unchanged in kind, only de-branded (see `archived provenance ledger` for the full
9
10
  * field-mapping table and provenance).
10
11
  *
11
12
  * Folded in 2026-07-26 from the standalone `@jini-ai/agui` package (plan §3a) into this `src/gen-ui/`
@@ -1,11 +1,12 @@
1
1
  /**
2
- * @module agui/events
2
+ * @module gen-ui/events
3
3
  *
4
- * The AG-UI wire event shapes `./encoder.ts` produces. AG-UI (Agent-User Interaction
5
- * Protocol) is CopilotKit's open, external wire protocol for streaming an agent's run over SSE to
6
- * a UI client — see https://github.com/ag-ui-protocol/ag-ui. This module is a near-verbatim port
4
+ * The custom gen-ui wire event shapes `./encoder.ts` produces. AG-UI (Agent-User Interaction
5
+ * Protocol) is CopilotKit's separate, external wire protocol for streaming an agent's run over SSE to
6
+ * a UI client — see https://github.com/ag-ui-protocol/ag-ui. These six `kind` values belong to
7
+ * Jini's own protocol, not the external AG-UI event vocabulary. This module is a near-verbatim port
7
8
  * of a 312-line adapter that encoded a product's own run-event stream into this shape; the types
8
- * themselves are unchanged in kind, only de-branded (see `source-map.md` for the full
9
+ * themselves are unchanged in kind, only de-branded (see `archived provenance ledger` for the full
9
10
  * field-mapping table and provenance).
10
11
  *
11
12
  * Folded in 2026-07-26 from the standalone `@jini-ai/agui` package (plan §3a) into this `src/gen-ui/`
@@ -8,7 +8,7 @@
8
8
  * **This is not AG-UI**, despite what this directory was called until 2026-07-27. The real
9
9
  * Agent-User Interaction Protocol uses a 33-member `SCREAMING_SNAKE` event enum and shares zero
10
10
  * event names with these six; `../ag-ui.ts` is the genuine AG-UI projection and lives at `src/`
11
- * root, apart from this. See `source-map.md` for how the two came to be confused.
11
+ * root, apart from this. See `archived provenance ledger` for how the two came to be confused.
12
12
  *
13
13
  * Nothing here opens a connection or touches a browser global. Transport (SSE, WebSocket) belongs
14
14
  * to a host — `@jini-ai/http-kit`'s run-stream route is the shipped consumer.
@@ -8,7 +8,7 @@
8
8
  * **This is not AG-UI**, despite what this directory was called until 2026-07-27. The real
9
9
  * Agent-User Interaction Protocol uses a 33-member `SCREAMING_SNAKE` event enum and shares zero
10
10
  * event names with these six; `../ag-ui.ts` is the genuine AG-UI projection and lives at `src/`
11
- * root, apart from this. See `source-map.md` for how the two came to be confused.
11
+ * root, apart from this. See `archived provenance ledger` for how the two came to be confused.
12
12
  *
13
13
  * Nothing here opens a connection or touches a browser global. Transport (SSE, WebSocket) belongs
14
14
  * to a host — `@jini-ai/http-kit`'s run-stream route is the shipped consumer.