@jini-ai/ui 0.4.4 → 0.4.5

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 (165) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +20 -0
  3. package/dist/core/theme/default.d.ts +4 -0
  4. package/dist/core/theme/default.js +11 -0
  5. package/dist/core/theme/stylesheet.d.ts +8 -0
  6. package/dist/core/theme/stylesheet.js +14 -0
  7. package/dist/core/theme/types.d.ts +39 -0
  8. package/dist/core/theme/types.js +2 -0
  9. package/dist/core/theme/validation.d.ts +15 -0
  10. package/dist/core/theme/validation.js +55 -0
  11. package/dist/features/admin-widgets/components/Select/Select.d.ts +11 -3
  12. package/dist/features/admin-widgets/components/Select/Select.hooks.d.ts +8 -13
  13. package/dist/features/admin-widgets/components/Select/Select.hooks.js +37 -215
  14. package/dist/features/admin-widgets/components/Select/Select.js +4 -3
  15. package/dist/features/appearance/constants.js +1 -1
  16. package/dist/features/connectors/rules.d.ts +1 -6
  17. package/dist/features/connectors/types.d.ts +1 -6
  18. package/dist/features/connectors/types.js +1 -6
  19. package/dist/features/execution/http-adapter.d.ts +111 -0
  20. package/dist/features/execution/http-adapter.js +160 -0
  21. package/dist/features/execution/index.d.ts +4 -0
  22. package/dist/features/execution/index.js +2 -0
  23. package/dist/features/execution/react/hooks/use-execution-credential.hooks.d.ts +232 -0
  24. package/dist/features/execution/react/hooks/use-execution-credential.hooks.js +217 -0
  25. package/dist/features/html-editor/canvas-style.d.ts +1 -1
  26. package/dist/features/html-editor/canvas-style.js +2 -2
  27. package/dist/features/i18n/dictionaries/settings-dialog.ar.js +2 -0
  28. package/dist/features/i18n/dictionaries/settings-dialog.bn.js +2 -0
  29. package/dist/features/i18n/dictionaries/settings-dialog.de.js +2 -0
  30. package/dist/features/i18n/dictionaries/settings-dialog.en.js +2 -0
  31. package/dist/features/i18n/dictionaries/settings-dialog.es.js +2 -0
  32. package/dist/features/i18n/dictionaries/settings-dialog.fa.js +2 -0
  33. package/dist/features/i18n/dictionaries/settings-dialog.fr.js +2 -0
  34. package/dist/features/i18n/dictionaries/settings-dialog.hi.js +2 -0
  35. package/dist/features/i18n/dictionaries/settings-dialog.hu.js +2 -0
  36. package/dist/features/i18n/dictionaries/settings-dialog.id.js +2 -0
  37. package/dist/features/i18n/dictionaries/settings-dialog.it.js +2 -0
  38. package/dist/features/i18n/dictionaries/settings-dialog.ja.js +2 -0
  39. package/dist/features/i18n/dictionaries/settings-dialog.ko.js +2 -0
  40. package/dist/features/i18n/dictionaries/settings-dialog.pl.js +2 -0
  41. package/dist/features/i18n/dictionaries/settings-dialog.pt-BR.js +2 -0
  42. package/dist/features/i18n/dictionaries/settings-dialog.ru.js +2 -0
  43. package/dist/features/i18n/dictionaries/settings-dialog.th.js +2 -0
  44. package/dist/features/i18n/dictionaries/settings-dialog.tr.js +2 -0
  45. package/dist/features/i18n/dictionaries/settings-dialog.uk.js +2 -0
  46. package/dist/features/i18n/dictionaries/settings-dialog.ur.js +2 -0
  47. package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +2 -0
  48. package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +2 -0
  49. package/dist/features/i18n/dictionary-translator.d.ts +5 -2
  50. package/dist/features/i18n/dictionary-translator.js +1 -1
  51. package/dist/features/i18n/index.d.ts +1 -1
  52. package/dist/features/i18n/index.js +1 -1
  53. package/dist/features/i18n/template-i18n.d.ts +11 -0
  54. package/dist/features/i18n/template-i18n.js +12 -0
  55. package/dist/features/interactive-ui/providers/recharts/chart-tokens.d.ts +1 -1
  56. package/dist/features/interactive-ui/providers/recharts/chart-tokens.js +1 -1
  57. package/dist/features/lexical-rich-text-editor/deserialize.d.ts +0 -5
  58. package/dist/features/lexical-rich-text-editor/deserialize.js +0 -5
  59. package/dist/features/lexical-rich-text-editor/mention-node.d.ts +5 -17
  60. package/dist/features/lexical-rich-text-editor/mention-node.js +5 -17
  61. package/dist/features/lexical-rich-text-editor/mention-parser.d.ts +2 -4
  62. package/dist/features/lexical-rich-text-editor/serialize.d.ts +0 -4
  63. package/dist/features/lexical-rich-text-editor/serialize.js +0 -4
  64. package/dist/features/lexical-rich-text-editor/types.d.ts +3 -13
  65. package/dist/features/lexical-rich-text-editor/types.js +3 -13
  66. package/dist/features/mcp-ui/early-message-buffer.d.ts +1 -2
  67. package/dist/features/mcp-ui/early-message-buffer.js +1 -2
  68. package/dist/features/mcp-ui/escape.d.ts +4 -0
  69. package/dist/features/mcp-ui/escape.js +2 -9
  70. package/dist/features/mcp-ui/index.d.ts +1 -0
  71. package/dist/features/mcp-ui/index.js +1 -0
  72. package/dist/features/mcp-ui/protocol.d.ts +2 -5
  73. package/dist/features/mcp-ui/protocol.js +2 -5
  74. package/dist/features/mcp-ui/resource.d.ts +47 -7
  75. package/dist/features/mcp-ui/resource.js +5 -8
  76. package/dist/features/mcp-ui/secret-card.d.ts +115 -0
  77. package/dist/features/mcp-ui/secret-card.js +137 -0
  78. package/dist/features/mcp-ui/surfaces/confirmation.js +5 -0
  79. package/dist/features/mcp-ui/surfaces/fields.d.ts +5 -2
  80. package/dist/features/mcp-ui/surfaces/fields.js +3 -1
  81. package/dist/features/mcp-ui/surfaces/form.d.ts +4 -1
  82. package/dist/features/mcp-ui/surfaces/form.js +65 -3
  83. package/dist/features/mcp-ui/surfaces/outcome.d.ts +2 -0
  84. package/dist/features/mcp-ui/surfaces/text-input.d.ts +14 -6
  85. package/dist/features/mcp-ui/surfaces/text-input.js +16 -2
  86. package/dist/features/mcp-ui/surfaces/tokens.js +1 -1
  87. package/dist/features/memory/react/components/MemoryConnectedPanel.d.ts +1 -1
  88. package/dist/features/memory/react/components/MemoryConnectedPanel.js +0 -1
  89. package/dist/features/panel-kit/helpers/format-timestamp.d.ts +5 -3
  90. package/dist/features/panel-kit/helpers/format-timestamp.js +13 -4
  91. package/dist/features/panel-kit/helpers/permissions.js +2 -0
  92. package/dist/features/panel-kit/helpers/resolve-active-tab-id.js +2 -0
  93. package/dist/features/panel-kit/helpers/standing-draft-local-backup.d.ts +51 -0
  94. package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +77 -0
  95. package/dist/features/panel-kit/helpers/standing-draft-recovery-message.d.ts +8 -0
  96. package/dist/features/panel-kit/helpers/standing-draft-recovery-message.js +10 -0
  97. package/dist/features/panel-kit/hooks/use-async-action.hooks.d.ts +1 -0
  98. package/dist/features/panel-kit/hooks/use-dirty-guard.hooks.d.ts +1 -0
  99. package/dist/features/panel-kit/hooks/use-dirty-guard.hooks.js +1 -0
  100. package/dist/features/panel-kit/hooks/use-serial-writes.hooks.d.ts +8 -0
  101. package/dist/features/panel-kit/hooks/use-serial-writes.hooks.js +3 -0
  102. package/dist/features/panel-kit/hooks/use-settings-slice.hooks.d.ts +211 -0
  103. package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +382 -0
  104. package/dist/features/panel-kit/hooks/use-standing-draft-autosave.hooks.d.ts +179 -0
  105. package/dist/features/panel-kit/hooks/use-standing-draft-autosave.hooks.js +300 -0
  106. package/dist/features/panel-kit/index.d.ts +7 -1
  107. package/dist/features/panel-kit/index.js +5 -1
  108. package/dist/features/resource-dashboard/types.d.ts +8 -41
  109. package/dist/features/resource-dashboard/types.js +3 -26
  110. package/dist/features/tab-strip/constants.d.ts +3 -4
  111. package/dist/features/tab-strip/constants.js +3 -4
  112. package/dist/features/tab-strip/dependencies.d.ts +1 -6
  113. package/dist/features/tab-strip/dependencies.js +1 -6
  114. package/dist/features/tab-strip/react/components/TabBar.d.ts +33 -1
  115. package/dist/features/tab-strip/react/components/TabBar.hooks.d.ts +57 -5
  116. package/dist/features/tab-strip/react/components/TabBar.hooks.js +63 -5
  117. package/dist/features/tab-strip/react/components/TabBar.js +12 -1
  118. package/dist/features/tab-strip/rules.d.ts +4 -17
  119. package/dist/features/tab-strip/rules.js +4 -17
  120. package/dist/features/tab-strip/types.d.ts +2 -13
  121. package/dist/icon-name.d.ts +1 -1
  122. package/dist/icon-name.js +1 -0
  123. package/dist/index.d.ts +42 -417
  124. package/dist/index.js +42 -430
  125. package/dist/react/components/CustomSelect.d.ts +6 -2
  126. package/dist/react/components/CustomSelect.js +20 -91
  127. package/dist/react/components/Icon.d.ts +6 -0
  128. package/dist/react/components/Icon.js +12 -0
  129. package/dist/react/components/select-popover.d.ts +106 -0
  130. package/dist/react/components/select-popover.js +210 -0
  131. package/dist/renderers/sandbox-scripts.d.ts +29 -0
  132. package/dist/renderers/sandbox-scripts.js +127 -0
  133. package/dist/renderers/sandboxed-document.js +3 -86
  134. package/dist/renderers/srcdoc/build.d.ts +10 -17
  135. package/dist/renderers/srcdoc/build.js +31 -234
  136. package/dist/styles/variables.css +2 -2
  137. package/dist/theme/apply.d.ts +1 -1
  138. package/dist/theme/apply.js +2 -2
  139. package/dist/theme/color-scheme.d.ts +1 -1
  140. package/dist/theme/default.d.ts +1 -3
  141. package/dist/theme/default.js +2 -10
  142. package/dist/theme/index.d.ts +3 -3
  143. package/dist/theme/index.js +2 -2
  144. package/dist/theme/stylesheet.d.ts +1 -7
  145. package/dist/theme/stylesheet.js +2 -13
  146. package/dist/theme/types.d.ts +1 -38
  147. package/dist/theme/types.js +2 -1
  148. package/dist/theme/validation.d.ts +1 -14
  149. package/dist/theme/validation.js +2 -54
  150. package/dist/utils/endpoint-policy.d.ts +21 -39
  151. package/dist/utils/endpoint-policy.js +19 -107
  152. package/dist/utils/file-system-errors.d.ts +1 -2
  153. package/dist/utils/file-system-errors.js +1 -2
  154. package/dist/utils/ime-composing.d.ts +0 -3
  155. package/dist/utils/ime-composing.js +0 -3
  156. package/dist/utils/platform.d.ts +1 -4
  157. package/dist/utils/platform.js +1 -4
  158. package/dist/utils/smooth-scroll-to-top.d.ts +1 -3
  159. package/dist/utils/smooth-scroll-to-top.js +3 -13
  160. package/dist/utils/timezone.js +1 -5
  161. package/dist/utils/uuid.d.ts +0 -3
  162. package/dist/utils/uuid.js +0 -3
  163. package/dist/utils/visual-stability.d.ts +1 -3
  164. package/dist/utils/visual-stability.js +1 -3
  165. package/package.json +74 -15
@@ -1,21 +1,9 @@
1
1
  /**
2
- * The atomic `@mention` node. Extends `TextNode` so the node's *text* remains
3
- * the literal `@token` and serialization back to the wire format is free:
4
- * `getTextContent()` already yields `@token`. Lexical token mode deletes the
5
- * node as one entity but still allows character-by-character caret
6
- * navigation, so `rules.ts` adds explicit keyboard normalization for arrows.
7
- *
8
- * Origin: `apps/web/src/components/composer/MentionNode.ts`. The origin
9
- * imported OD's `connectorBrandColor`/`resolveBrandTheme` directly to color
10
- * a `connector`-kind pill's `--m-hue` CSS custom property, and installed a
11
- * document-wide `MutationObserver` to re-stamp every mounted pill on a live
12
- * theme flip. Both are dropped from this node entirely — no per-instance
13
- * color logic and no global observer belong on a generic Lexical node class.
14
- * A host that wants per-kind/per-entity coloring supplies a
15
- * `resolveMentionColor` callback to `RichTextInput`, applied by
16
- * `react/hooks/useMentionColorStamping.ts` via Lexical's own
17
- * `registerMutationListener` (scoped to that one editor instance, not the
18
- * whole document) — see that file for the replacement mechanism.
2
+ * Atomic @mention extends TextNode so getTextContent preserves the wire token. Token mode deletes
3
+ * one entity but still permits character-level caret movement; rules.ts normalizes arrow navigation.
4
+ * Generic nodes own no brand resolution or document-wide observer. Host colors enter through
5
+ * RichTextInput.resolveMentionColor and an editor-scoped Lexical mutation listener; see
6
+ * react/hooks/useMentionColorStamping.ts.
19
7
  */
20
8
  import { TextNode, } from 'lexical';
21
9
  export class MentionNode extends TextNode {
@@ -5,10 +5,8 @@
5
5
  * mention nodes, and available to hosts that need the same rule to fold
6
6
  * plain-text-typed `@token`s into a "currently referenced" entity list.
7
7
  *
8
- * Origin: `apps/web/src/utils/inlineMentions.ts`. Ported verbatim (trie-based
9
- * longest-match lookup, left/right boundary rules) — the only OD-specific
10
- * surface was `InlineMentionKind`'s fixed union, replaced by `MentionEntity`'s
11
- * free-form `kind: string` (see `types.ts`).
8
+ * Trie lookup prefers the longest token subject to left/right boundaries. MentionEntity.kind
9
+ * is host-defined rather than a fixed product vocabulary.
12
10
  */
13
11
  import type { MentionEntity, MentionPart } from './types.js';
14
12
  export declare function buildMentionToken(label: string): string;
@@ -1,7 +1,3 @@
1
- /**
2
- * Origin: `apps/web/src/components/composer/serialize.ts`. Ported verbatim —
3
- * no OD-specific surface (it only ever touched the generic `MentionNode`).
4
- */
5
1
  import { type EditorState } from 'lexical';
6
2
  import type { SerializedRichText } from './types.js';
7
3
  export declare function serializeRichText(state: EditorState): SerializedRichText;
@@ -1,7 +1,3 @@
1
- /**
2
- * Origin: `apps/web/src/components/composer/serialize.ts`. Ported verbatim —
3
- * no OD-specific surface (it only ever touched the generic `MentionNode`).
4
- */
5
1
  import { $getRoot, $isElementNode, $isLineBreakNode, $isParagraphNode, $isTextNode, } from 'lexical';
6
2
  import { $isMentionNode } from './mention-node.js';
7
3
  // Walk the tree by hand rather than calling `root.getTextContent()`, which
@@ -1,16 +1,6 @@
1
- /**
2
- * Generic types for a Lexical-backed rich text input: a plain-text-with-
3
- * atomic-mention-tokens editor (not a full block/marks rich text editor —
4
- * the origin used Lexical's `PlainTextPlugin`, not `RichTextPlugin`).
5
- *
6
- * Origin: `apps/web/src/components/composer/*` (OD's chat composer). The
7
- * only OD-specific piece was the `@mention` pill's brand color, resolved
8
- * via a direct import of OD's connector-brand-theme logic — replaced here
9
- * by a host-injected `resolveMentionColor` callback (see
10
- * `react/hooks/useMentionColorStamping.ts`). Everything else (trigger
11
- * detection, atomic-node keyboard navigation, caret-floating-layer
12
- * positioning, serialize/deserialize) is genuinely generic and ported as-is.
13
- */
1
+ /** PlainTextPlugin-backed editing with atomic mentions, rather than block/mark rich text.
2
+ * Mention colors are host-injected through resolveMentionColor; generic trigger/navigation/caret
3
+ * and wire serialization rules stay independent of a product's connector branding. */
14
4
  /** A single mention-able entity: any host-owned capability/entity with a
15
5
  * stable id, a display label, and a free-form category ("kind"). Unlike
16
6
  * the origin's fixed `plugin | skill | mcp | file | workspace | connector`
@@ -1,15 +1,5 @@
1
- /**
2
- * Generic types for a Lexical-backed rich text input: a plain-text-with-
3
- * atomic-mention-tokens editor (not a full block/marks rich text editor —
4
- * the origin used Lexical's `PlainTextPlugin`, not `RichTextPlugin`).
5
- *
6
- * Origin: `apps/web/src/components/composer/*` (OD's chat composer). The
7
- * only OD-specific piece was the `@mention` pill's brand color, resolved
8
- * via a direct import of OD's connector-brand-theme logic — replaced here
9
- * by a host-injected `resolveMentionColor` callback (see
10
- * `react/hooks/useMentionColorStamping.ts`). Everything else (trigger
11
- * detection, atomic-node keyboard navigation, caret-floating-layer
12
- * positioning, serialize/deserialize) is genuinely generic and ported as-is.
13
- */
1
+ /** PlainTextPlugin-backed editing with atomic mentions, rather than block/mark rich text.
2
+ * Mention colors are host-injected through resolveMentionColor; generic trigger/navigation/caret
3
+ * and wire serialization rules stay independent of a product's connector branding. */
14
4
  export {};
15
5
  //# sourceMappingURL=types.js.map
@@ -16,8 +16,7 @@
16
16
  * DOM types and no React, so it is testable in a plain Node environment. The module-scope
17
17
  * `window.addEventListener` that feeds it belongs to the React half; see
18
18
  * `../../react/mcp-ui/host-message-source.ts`.
19
- *
20
- * Ported from `examples/reference-web/src/mcpui-lab-message-buffer.ts` unchanged in behavior.
19
+
21
20
  */
22
21
  /**
23
22
  * A raw `MessageEvent`, narrowed to the fields this module needs. `source` stays `unknown` (rather
@@ -16,8 +16,7 @@
16
16
  * DOM types and no React, so it is testable in a plain Node environment. The module-scope
17
17
  * `window.addEventListener` that feeds it belongs to the React half; see
18
18
  * `../../react/mcp-ui/host-message-source.ts`.
19
- *
20
- * Ported from `examples/reference-web/src/mcpui-lab-message-buffer.ts` unchanged in behavior.
19
+
21
20
  */
22
21
  /**
23
22
  * Bound so a hostile or confused page flooding `postMessage` at a tab that never subscribes cannot
@@ -47,5 +47,9 @@ export declare function escapeJsValue(value: unknown): string;
47
47
  * `string`-typed signature makes a caller that accidentally passes an object a compile error rather
48
48
  * than a silently-inlined `[object Object]`-free-but-surprising literal.
49
49
  */
50
+ export declare function escapeJsString(required: {
51
+ value: string;
52
+ }, optional?: {}): string;
53
+ /** Existing string call retained for published surface builders. */
50
54
  export declare function escapeJsString(value: string): string;
51
55
  //# sourceMappingURL=escape.d.ts.map
@@ -55,14 +55,7 @@ export function escapeJsValue(value) {
55
55
  .replace(/\u2028/g, '\\u2028')
56
56
  .replace(/\u2029/g, '\\u2029');
57
57
  }
58
- /**
59
- * {@link escapeJsValue} narrowed to strings.
60
- *
61
- * Kept as its own export because the overwhelmingly common call is "inline this one string", and a
62
- * `string`-typed signature makes a caller that accidentally passes an object a compile error rather
63
- * than a silently-inlined `[object Object]`-free-but-surprising literal.
64
- */
65
- export function escapeJsString(value) {
66
- return escapeJsValue(value);
58
+ export function escapeJsString(required, _optional = {}) {
59
+ return escapeJsValue(typeof required === "string" ? required : required.value);
67
60
  }
68
61
  //# sourceMappingURL=escape.js.map
@@ -23,6 +23,7 @@ export * from './early-message-buffer.js';
23
23
  export * from './escape.js';
24
24
  export * from './protocol.js';
25
25
  export * from './resource.js';
26
+ export * from './secret-card.js';
26
27
  export * from './sandbox-proxy.js';
27
28
  export * from './surfaces/index.js';
28
29
  //# sourceMappingURL=index.d.ts.map
@@ -23,6 +23,7 @@ export * from './early-message-buffer.js';
23
23
  export * from './escape.js';
24
24
  export * from './protocol.js';
25
25
  export * from './resource.js';
26
+ export * from './secret-card.js';
26
27
  export * from './sandbox-proxy.js';
27
28
  export * from './surfaces/index.js';
28
29
  //# sourceMappingURL=index.js.map
@@ -4,11 +4,8 @@
4
4
  * The MCP Apps wire vocabulary, re-exported from `@jini-ai/agentic`'s `mcp-ui-apps.ts` rather than
5
5
  * restated here.
6
6
  *
7
- * `mcp-ui-apps.ts` is the spec-verified module (re-checked 2026-07-28 against
8
- * `github.com/modelcontextprotocol/ext-apps`, both the 2026-01-26 [Stable] text and the draft), and
9
- * it already carries the two things a second copy would immediately lose: the audit history for
10
- * which lifecycle messages travel in which direction, and `isJsonRpcMessage`'s hardening against
11
- * malformed `postMessage` input found by live adversarial testing. Duplicating any of it would
7
+ * `mcp-ui-apps.ts` owns message direction and isJsonRpcMessage's malformed-postMessage guards.
8
+ * Duplicating those rules would
12
9
  * create a second surface to keep in sync with a moving spec, so this module owns nothing of the
13
10
  * envelope — it only re-exports it, plus adds the one constant that genuinely belongs to *this*
14
11
  * package: {@link MCP_UI_VIEW_SANDBOX}.
@@ -4,11 +4,8 @@
4
4
  * The MCP Apps wire vocabulary, re-exported from `@jini-ai/agentic`'s `mcp-ui-apps.ts` rather than
5
5
  * restated here.
6
6
  *
7
- * `mcp-ui-apps.ts` is the spec-verified module (re-checked 2026-07-28 against
8
- * `github.com/modelcontextprotocol/ext-apps`, both the 2026-01-26 [Stable] text and the draft), and
9
- * it already carries the two things a second copy would immediately lose: the audit history for
10
- * which lifecycle messages travel in which direction, and `isJsonRpcMessage`'s hardening against
11
- * malformed `postMessage` input found by live adversarial testing. Duplicating any of it would
7
+ * `mcp-ui-apps.ts` owns message direction and isJsonRpcMessage's malformed-postMessage guards.
8
+ * Duplicating those rules would
12
9
  * create a second surface to keep in sync with a moving spec, so this module owns nothing of the
13
10
  * envelope — it only re-exports it, plus adds the one constant that genuinely belongs to *this*
14
11
  * package: {@link MCP_UI_VIEW_SANDBOX}.
@@ -12,11 +12,8 @@
12
12
  * ``ui://${string}`` URI scheme and the wider MIME union reproduced in
13
13
  * {@link UIResourceMimeType}.
14
14
  *
15
- * Ported from the reference implementation's `mcp-ui.ts`, minus that file's hand-rolled copy of the
16
- * JSON-RPC envelope and its `UIActionResult` union: the reference implementation declared those
17
- * because its tsconfig's
18
- * `moduleResolution: "Node"` cannot read a modern package's `exports` map, and this package has no
19
- * such constraint — the envelope comes from `protocol.ts`'s re-exports instead. See
15
+ * The envelope comes from protocol.ts's re-exports, so this resource layer has no parallel
16
+ * JSON-RPC or UIActionResult definition. See
20
17
  * `surfaces/bridge.ts` for why the legacy `{type:'tool', payload}` action shape is not emitted.
21
18
  */
22
19
  /** The `ui://` URI scheme MCP Apps fixes for UI resources. */
@@ -153,7 +150,7 @@ export declare function createUIResource(spec: {
153
150
  actionPlan?: McpUiActionPlan;
154
151
  expiresAtMs?: number;
155
152
  meta?: Readonly<Record<string, unknown>>;
156
- }): UIResource;
153
+ }, _optional?: {}): UIResource;
157
154
  /**
158
155
  * Assembles the two-part tool result: what the MODEL is told, and what the HUMAN is shown.
159
156
  *
@@ -167,7 +164,7 @@ export declare function buildUIToolResult(spec: {
167
164
  modelText: string;
168
165
  ui: UIResource;
169
166
  meta?: Readonly<Record<string, unknown>>;
170
- }): UIToolResult;
167
+ }, _optional?: {}): UIToolResult;
171
168
  /**
172
169
  * Narrows an arbitrary value to a {@link UIResource}.
173
170
  *
@@ -209,4 +206,47 @@ export declare function readActionPlan(resource: UIResource): McpUiActionPlan |
209
206
  * @complexity O(1).
210
207
  */
211
208
  export declare function readExpiresAt(resource: UIResource): number | undefined;
209
+ /** Legacy host action vocabulary retained for consumers that negotiate pre-Apps messages. */
210
+ export type UIActionType = "tool" | "prompt" | "link" | "intent" | "notify";
211
+ /**
212
+ * mcp-ui's `UIActionResult` union — the `postMessage` payload an iframe sends to the host.
213
+ *
214
+ * `messageId` is what makes the round trip observable: mcp-ui's client answers a message carrying
215
+ * one with `ui-message-received` and then `ui-message-response` (same `messageId`). A host's
216
+ * confirmation dialog sets one so the rendered UI can show "deleted" or an error instead of
217
+ * silently appearing to do nothing.
218
+ */
219
+ export type UIActionResult = {
220
+ messageId?: string;
221
+ type: "tool";
222
+ payload: {
223
+ toolName: string;
224
+ params: Record<string, unknown>;
225
+ };
226
+ } | {
227
+ messageId?: string;
228
+ type: "prompt";
229
+ payload: {
230
+ prompt: string;
231
+ };
232
+ } | {
233
+ messageId?: string;
234
+ type: "link";
235
+ payload: {
236
+ url: string;
237
+ };
238
+ } | {
239
+ messageId?: string;
240
+ type: "intent";
241
+ payload: {
242
+ intent: string;
243
+ params: Record<string, unknown>;
244
+ };
245
+ } | {
246
+ messageId?: string;
247
+ type: "notify";
248
+ payload: {
249
+ message: string;
250
+ };
251
+ };
212
252
  //# sourceMappingURL=resource.d.ts.map
@@ -12,11 +12,8 @@
12
12
  * ``ui://${string}`` URI scheme and the wider MIME union reproduced in
13
13
  * {@link UIResourceMimeType}.
14
14
  *
15
- * Ported from the reference implementation's `mcp-ui.ts`, minus that file's hand-rolled copy of the
16
- * JSON-RPC envelope and its `UIActionResult` union: the reference implementation declared those
17
- * because its tsconfig's
18
- * `moduleResolution: "Node"` cannot read a modern package's `exports` map, and this package has no
19
- * such constraint — the envelope comes from `protocol.ts`'s re-exports instead. See
15
+ * The envelope comes from protocol.ts's re-exports, so this resource layer has no parallel
16
+ * JSON-RPC or UIActionResult definition. See
20
17
  * `surfaces/bridge.ts` for why the legacy `{type:'tool', payload}` action shape is not emitted.
21
18
  */
22
19
  /**
@@ -94,7 +91,7 @@ export const MCP_UI_EXPIRES_AT_META_KEY = `${JINI_MCP_UI_METADATA_PREFIX}expires
94
91
  * @returns The `EmbeddedResource` to place in a tool result's `content` array.
95
92
  * @complexity O(1).
96
93
  */
97
- export function createUIResource(spec) {
94
+ export function createUIResource(spec, _optional = {}) {
98
95
  const meta = {
99
96
  ...(spec.preferredFrameSize === undefined
100
97
  ? {}
@@ -109,7 +106,7 @@ export function createUIResource(spec) {
109
106
  uri: spec.uri,
110
107
  mimeType: MCP_UI_MIME_TYPE,
111
108
  text: spec.htmlString,
112
- ...(Object.keys(meta).length === 0 ? {} : { _meta: meta }),
109
+ ...(Object.keys(meta).length === 0 && spec.meta === undefined ? {} : { _meta: meta }),
113
110
  },
114
111
  };
115
112
  }
@@ -122,7 +119,7 @@ export function createUIResource(spec) {
122
119
  * @param spec.meta - Optional result-level `_meta`.
123
120
  * @complexity O(1).
124
121
  */
125
- export function buildUIToolResult(spec) {
122
+ export function buildUIToolResult(spec, _optional = {}) {
126
123
  return {
127
124
  content: [{ type: 'text', text: spec.modelText }, spec.ui],
128
125
  ...(spec.meta === undefined ? {} : { _meta: spec.meta }),
@@ -0,0 +1,115 @@
1
+ /**
2
+ * One human-only secret-card lifecycle. Keystrokes go directly to the held exchange and save
3
+ * port, never through model input, chat text, or logs. Saved values must stay in the host's
4
+ * sealed store; save returns only a safe summary for result/outcome projections.
5
+ *
6
+ * Fail closed without an emitter: returning a form or accepting a second model call would
7
+ * strand the held call or route a secret through model context. Cancel posts a dismissal so
8
+ * the call wakes immediately instead of waiting for its TTL. askThenReport keeps the exchange
9
+ * open until the real save finishes; reusing the form URI replaces premature "Done." with its
10
+ * actual outcome. The host supplies the daemon's existing helper, never a second store.
11
+ */
12
+ import { type SurfaceAskThenReport, type SurfaceExchangeStore, type ToolExecutionContext, type ToolExecutionOptions } from '@jini-ai/core';
13
+ import { type FormSurfaceSpec } from './surfaces/form.js';
14
+ import { type SurfaceOutcomeSpec } from './surfaces/outcome.js';
15
+ import type { StringField, SurfaceField } from './surfaces/fields.js';
16
+ /** Secret fields cannot carry a value, even through a previously declared variable.
17
+ * `allowBlank` permits an empty string for stored-value preservation or public-client auth. */
18
+ export type SecretCardField = (SurfaceField & {
19
+ readonly secret?: false;
20
+ }) | (Omit<StringField, 'value' | 'secret'> & {
21
+ readonly secret: true;
22
+ readonly value?: never;
23
+ readonly allowBlank?: boolean;
24
+ });
25
+ /** The host controls copy, field construction and display; routing/dismissal belong to the engine. */
26
+ export type SecretCardForm = Omit<FormSurfaceSpec, 'fields' | 'toolName' | 'baseParams' | 'cancel'> & {
27
+ readonly fields: readonly SecretCardField[];
28
+ readonly cancelLabel?: string;
29
+ };
30
+ export type SecretCardRun<Saved> = {
31
+ readonly status: 'saved';
32
+ readonly saved: Saved;
33
+ } | {
34
+ readonly status: 'cancelled';
35
+ } | {
36
+ readonly status: 'expired';
37
+ } | {
38
+ readonly status: 'abandoned';
39
+ } | {
40
+ readonly status: 'blank';
41
+ } | {
42
+ readonly status: 'failed';
43
+ readonly safeMessage: string;
44
+ };
45
+ /** A domain spec, with authorization and target resolution in prepare and persistence in save.
46
+ * Projections must contain safe metadata only. Neither projection receives the submitted values. */
47
+ export interface SecretCardSpec<Prep, Saved, Result> {
48
+ readonly toolId: string;
49
+ /** Validate public model input, authorize and resolve the target before opening a card.
50
+ * Reject secret-bearing input; never read stored plaintext for form construction. */
51
+ prepare(required: {
52
+ ctx: ToolExecutionContext;
53
+ }): Promise<Prep>;
54
+ form(required: {
55
+ prep: Prep;
56
+ }): SecretCardForm;
57
+ /** Only declared field names are forwarded. Domain validation remains authoritative.
58
+ * Values keep their native field types and exact bytes; stored secrets are never needed here. */
59
+ save(required: {
60
+ values: Readonly<Record<string, unknown>>;
61
+ prep: Prep;
62
+ signal: AbortSignal;
63
+ }): Promise<Saved>;
64
+ /** Return undefined for cancellation/expiry when the form already reports that state locally. */
65
+ outcome(required: {
66
+ prep: Prep;
67
+ run: SecretCardRun<Saved>;
68
+ }): SurfaceOutcomeSpec | undefined;
69
+ result(required: {
70
+ prep: Prep;
71
+ run: SecretCardRun<Saved>;
72
+ }): Result;
73
+ }
74
+ export interface SecretCardText {
75
+ readonly noEmitter?: string;
76
+ readonly saveFailure?: string;
77
+ readonly cancelLabel?: string;
78
+ }
79
+ export interface SecretCardOptions {
80
+ /** Allowlist known error kinds to fixed text. Never return raw error.message. */
81
+ readonly safeError?: (error: unknown) => string | undefined;
82
+ readonly text?: SecretCardText;
83
+ readonly frameSize?: readonly [string, string];
84
+ readonly uriHost?: string;
85
+ /** Fixed metadata only: errors and submitted values are deliberately absent. */
86
+ readonly logFailure?: (metadata: {
87
+ toolId: string;
88
+ exchangeId: string;
89
+ saved: false;
90
+ }) => void;
91
+ }
92
+ export interface SecretCardDeps {
93
+ readonly surfaceExchanges: Pick<SurfaceExchangeStore, 'open'>;
94
+ readonly askThenReport: SurfaceAskThenReport;
95
+ }
96
+ export type SecretCardHandler<Result> = (required: ToolExecutionContext, optional?: ToolExecutionOptions) => Promise<Result>;
97
+ export interface SecretCardTool<Prep, Result> {
98
+ readonly toolId: string;
99
+ secretFieldNames(prep: Prep): string[];
100
+ handler(deps: SecretCardDeps): SecretCardHandler<Result>;
101
+ }
102
+ /**
103
+ * Define a secret card once; inject the exchange store and daemon askThenReport at composition.
104
+ * @param required - Domain fields, permission/target checks, save port and safe projections.
105
+ * @param options - Optional copy, error allowlist, fixed-metadata logger and resource framing.
106
+ * @returns Tool identity, dynamic secret-field metadata and a typed registry-compatible handler.
107
+ * @throws ToolInputError if the emitter is missing or fields violate the secret/routing contract.
108
+ * Prepare/transport failures propagate; save failures become a fixed safe outcome.
109
+ * @complexity O(n + h + b) locally in fields, rendered HTML and submitted secret bytes, plus host I/O.
110
+ * @example
111
+ * const tool = defineSecretCardTool(spec);
112
+ * registry.register({ descriptor, policy, handler: tool.handler({ surfaceExchanges, askThenReport }) });
113
+ */
114
+ export declare function defineSecretCardTool<Prep, Saved, Result>(required: SecretCardSpec<Prep, Saved, Result>, options?: SecretCardOptions): SecretCardTool<Prep, Result>;
115
+ //# sourceMappingURL=secret-card.d.ts.map
@@ -0,0 +1,137 @@
1
+ /**
2
+ * One human-only secret-card lifecycle. Keystrokes go directly to the held exchange and save
3
+ * port, never through model input, chat text, or logs. Saved values must stay in the host's
4
+ * sealed store; save returns only a safe summary for result/outcome projections.
5
+ *
6
+ * Fail closed without an emitter: returning a form or accepting a second model call would
7
+ * strand the held call or route a secret through model context. Cancel posts a dismissal so
8
+ * the call wakes immediately instead of waiting for its TTL. askThenReport keeps the exchange
9
+ * open until the real save finishes; reusing the form URI replaces premature "Done." with its
10
+ * actual outcome. The host supplies the daemon's existing helper, never a second store.
11
+ */
12
+ import { ToolInputError, redactSecrets, SURFACE_DISMISSED_PARAM, SURFACE_EXCHANGE_ID_PARAM, SURFACE_TYPED_ANSWER_PARAM, } from '@jini-ai/core';
13
+ import { buildFormSurface } from './surfaces/form.js';
14
+ import { buildOutcomeSurface } from './surfaces/outcome.js';
15
+ /** Reject ambiguous or protocol-reserved names and runtime attempts to prefill secrets.
16
+ * @complexity O(n) time/space in the field count. Throws fixed ToolInputError text only. */
17
+ function checkedFields(form) {
18
+ const names = new Set([SURFACE_EXCHANGE_ID_PARAM, SURFACE_DISMISSED_PARAM, SURFACE_TYPED_ANSWER_PARAM]);
19
+ for (const field of form.fields) {
20
+ if (!field.name || names.has(field.name))
21
+ throw new ToolInputError({ message: 'Secret card field names must be unique and cannot use exchange parameters.' });
22
+ names.add(field.name);
23
+ if (field.secret === true && field.value !== undefined)
24
+ throw new ToolInputError({ message: 'Secret fields cannot have a pre-filled value. Omit value.' });
25
+ }
26
+ // Snapshot policies before waiting: host mutation must not change which submitted fields are secret.
27
+ return form.fields.map(field => ({ ...field }));
28
+ }
29
+ /** Classify before persistence; an abort wins over an already-buffered answer.
30
+ * Trimming tests blankness only: rewriting a provider secret can corrupt intentional newlines.
31
+ * @complexity O(n + b) before save in fields and submitted secret bytes; failure text delegates
32
+ * to core's exact-secret redaction scan. Host persistence cost is external. */
33
+ async function saveAnswer(required) {
34
+ const { answer, fields, prep, ctx, spec, options, exchangeId } = required;
35
+ if (ctx.signal.aborted)
36
+ return { status: 'abandoned' };
37
+ if (answer.status !== 'received')
38
+ return { status: answer.status };
39
+ if (answer.params[SURFACE_DISMISSED_PARAM] === true)
40
+ return { status: 'cancelled' };
41
+ const values = Object.fromEntries(fields.map(field => [field.name, answer.params[field.name]]));
42
+ const secrets = [];
43
+ for (const field of fields) {
44
+ if (field.secret !== true)
45
+ continue;
46
+ const value = values[field.name];
47
+ if (value !== undefined && typeof value !== 'string')
48
+ return { status: 'blank' };
49
+ const secret = typeof value === 'string' ? value : '';
50
+ if (secret.trim() === '' && (secret !== '' || field.allowBlank !== true))
51
+ return { status: 'blank' };
52
+ values[field.name] = secret;
53
+ secrets.push(secret);
54
+ }
55
+ if (ctx.signal.aborted)
56
+ return { status: 'abandoned' };
57
+ try {
58
+ return { status: 'saved', saved: await spec.save({ values, prep, signal: ctx.signal }) };
59
+ }
60
+ catch (error) {
61
+ if (ctx.signal.aborted)
62
+ return { status: 'abandoned' };
63
+ let safeMessage = options.text?.saveFailure ?? 'Saving failed. Nothing was saved.';
64
+ try {
65
+ safeMessage = options.safeError?.(error) ?? safeMessage;
66
+ }
67
+ catch { /* An error mapper must not disclose a second exception containing the submitted value. */ }
68
+ // Even an accidentally permissive allowlist must not echo the submitted secret.
69
+ safeMessage = redactSecrets({ input: safeMessage }, { exactSecrets: secrets });
70
+ try {
71
+ options.logFailure?.({ toolId: spec.toolId, exchangeId, saved: false });
72
+ }
73
+ catch { /* Logging must not replace the safe model result with an opaque logger exception. */ }
74
+ return { status: 'failed', safeMessage };
75
+ }
76
+ }
77
+ /**
78
+ * Define a secret card once; inject the exchange store and daemon askThenReport at composition.
79
+ * @param required - Domain fields, permission/target checks, save port and safe projections.
80
+ * @param options - Optional copy, error allowlist, fixed-metadata logger and resource framing.
81
+ * @returns Tool identity, dynamic secret-field metadata and a typed registry-compatible handler.
82
+ * @throws ToolInputError if the emitter is missing or fields violate the secret/routing contract.
83
+ * Prepare/transport failures propagate; save failures become a fixed safe outcome.
84
+ * @complexity O(n + h + b) locally in fields, rendered HTML and submitted secret bytes, plus host I/O.
85
+ * @example
86
+ * const tool = defineSecretCardTool(spec);
87
+ * registry.register({ descriptor, policy, handler: tool.handler({ surfaceExchanges, askThenReport }) });
88
+ */
89
+ export function defineSecretCardTool(required, options = {}) {
90
+ const spec = required;
91
+ return {
92
+ toolId: spec.toolId,
93
+ secretFieldNames: prep => checkedFields(spec.form({ prep })).filter(field => field.secret === true).map(field => field.name),
94
+ handler: deps => async (ctx, optional = {}) => {
95
+ if (!optional.emitSurface)
96
+ throw new ToolInputError({ message: options.text?.noEmitter ?? `${spec.toolId}: this execution context has no interactive form channel (no emitSurface), so this form cannot be shown here. Nothing was changed.` });
97
+ const prep = await spec.prepare({ ctx });
98
+ if (ctx.signal.aborted)
99
+ return spec.result({ prep, run: { status: 'abandoned' } });
100
+ const form = spec.form({ prep });
101
+ const fields = checkedFields(form);
102
+ if (ctx.signal.aborted)
103
+ return spec.result({ prep, run: { status: 'abandoned' } });
104
+ const exchange = deps.surfaceExchanges.open({ binding: { toolId: spec.toolId, principalId: ctx.principal.id, channel: 'mcp-ui' }, emit: optional.emitSurface });
105
+ const closeOnAbort = () => exchange.close({});
106
+ ctx.signal.addEventListener('abort', closeOnAbort, { once: true });
107
+ try {
108
+ // Close the open/register race as well as the prepare/abort race.
109
+ if (ctx.signal.aborted)
110
+ return spec.result({ prep, run: { status: 'abandoned' } });
111
+ const uri = `ui://${options.uriHost ?? 'jini'}/secret-card/${encodeURIComponent(spec.toolId)}/${encodeURIComponent(exchange.id)}`;
112
+ const framing = options.frameSize === undefined ? {} : { preferredFrameSize: options.frameSize };
113
+ const resource = buildFormSurface({ ...form, fields, uri, ...framing, toolName: spec.toolId,
114
+ baseParams: { [SURFACE_EXCHANGE_ID_PARAM]: exchange.id },
115
+ cancel: { label: form.cancelLabel ?? options.text?.cancelLabel ?? 'Cancel', toolName: spec.toolId,
116
+ params: { [SURFACE_EXCHANGE_ID_PARAM]: exchange.id, [SURFACE_DISMISSED_PARAM]: true } },
117
+ });
118
+ return await deps.askThenReport({ exchange, confirmationEmission: { channel: 'mcp-ui', payload: { resource } },
119
+ handle: async (answer) => {
120
+ const run = await saveAnswer({ answer, fields, prep, ctx, spec, options, exchangeId: exchange.id });
121
+ const result = spec.result({ prep, run });
122
+ const outcome = spec.outcome({ prep, run });
123
+ if (outcome === undefined)
124
+ return { result };
125
+ return { result, outcome: { channel: 'mcp-ui', payload: { resource: buildOutcomeSurface({ ...outcome, uri, ...framing }) } } };
126
+ },
127
+ });
128
+ }
129
+ finally {
130
+ ctx.signal.removeEventListener('abort', closeOnAbort);
131
+ // Also covers rendering/projection failures before or inside askThenReport; close is idempotent.
132
+ exchange.close({});
133
+ }
134
+ },
135
+ };
136
+ }
137
+ //# sourceMappingURL=secret-card.js.map
@@ -209,6 +209,11 @@ ${SURFACE_SCRIPT_PRELUDE}
209
209
  }
210
210
  onVisibilityChange();
211
211
  document.addEventListener("visibilitychange", onVisibilityChange);
212
+ // Published Jini started the dwell during document.write. If the iframe is hidden then, no
213
+ // timer is armed; becoming visible by sandbox initialization need not send visibilitychange
214
+ // to that new document. Re-check at the bridge ready boundary, preserving the original
215
+ // visibility, dwell, trusted-click and single-call gates. whenReady is already in every bridge.
216
+ api.whenReady(onVisibilityChange);
212
217
 
213
218
  for (var i = 0; i < actionButtons.length; i++) {
214
219
  actionButtons[i].addEventListener("click", onClick);
@@ -1,4 +1,5 @@
1
1
  import { type SelectOption } from './select.js';
2
+ import { type SecretInputText } from './text-input.js';
2
3
  interface SurfaceFieldBase {
3
4
  /** HTML `name`, DOM `id` suffix, and the params key the value is posted under. */
4
5
  readonly name: string;
@@ -14,7 +15,8 @@ export interface StringField extends SurfaceFieldBase {
14
15
  readonly placeholder?: string;
15
16
  readonly multiline?: boolean;
16
17
  readonly rows?: number;
17
- /** Renders as a masked `<input type="password">` — see {@link TextInputProps.secret}. */
18
+ /** Renders as a masked input or multiline textarea; pre-filled values are forbidden.
19
+ * See {@link TextInputProps.secret}. */
18
20
  readonly secret?: boolean;
19
21
  }
20
22
  export interface NumberField extends SurfaceFieldBase {
@@ -60,10 +62,11 @@ export type SurfaceField = StringField | NumberField | BooleanField | EnumField
60
62
  * Renders the control fragment for one field, dispatching on its kind.
61
63
  *
62
64
  * @param field - See {@link SurfaceField}.
65
+ * @param text - Optional host translations for secret reveal controls.
63
66
  * @returns An HTML fragment.
64
67
  * @complexity O(n) in the rendered length.
65
68
  */
66
- export declare function renderFieldControl(field: SurfaceField): string;
69
+ export declare function renderFieldControl(field: SurfaceField, text?: Partial<SecretInputText>): string;
67
70
  /** What the surface's inline script needs to read a field's value back and coerce it — the runtime half of {@link SurfaceField}, with the presentation stripped out. */
68
71
  export interface FieldReadSpec {
69
72
  readonly name: string;
@@ -32,14 +32,16 @@ function base(field) {
32
32
  * Renders the control fragment for one field, dispatching on its kind.
33
33
  *
34
34
  * @param field - See {@link SurfaceField}.
35
+ * @param text - Optional host translations for secret reveal controls.
35
36
  * @returns An HTML fragment.
36
37
  * @complexity O(n) in the rendered length.
37
38
  */
38
- export function renderFieldControl(field) {
39
+ export function renderFieldControl(field, text) {
39
40
  switch (field.kind) {
40
41
  case 'string':
41
42
  return renderTextInput({
42
43
  ...base(field),
44
+ ...text,
43
45
  ...(field.value === undefined ? {} : { value: field.value }),
44
46
  ...(field.placeholder === undefined ? {} : { placeholder: field.placeholder }),
45
47
  ...(field.multiline === undefined ? {} : { multiline: field.multiline }),