@runtypelabs/persona 3.17.0 → 3.18.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 (43) hide show
  1. package/README.md +142 -0
  2. package/dist/animations/glyph-cycle.d.cts +1 -1
  3. package/dist/animations/glyph-cycle.d.ts +1 -1
  4. package/dist/animations/{types-HPZY7oAI.d.cts → types-cwY5HaFD.d.cts} +25 -0
  5. package/dist/animations/{types-HPZY7oAI.d.ts → types-cwY5HaFD.d.ts} +25 -0
  6. package/dist/animations/wipe.d.cts +1 -1
  7. package/dist/animations/wipe.d.ts +1 -1
  8. package/dist/index.cjs +47 -47
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +300 -1
  11. package/dist/index.d.ts +300 -1
  12. package/dist/index.global.js +75 -75
  13. package/dist/index.global.js.map +1 -1
  14. package/dist/index.js +47 -47
  15. package/dist/index.js.map +1 -1
  16. package/dist/theme-editor.cjs +1432 -159
  17. package/dist/theme-editor.d.cts +218 -0
  18. package/dist/theme-editor.d.ts +218 -0
  19. package/dist/theme-editor.js +1432 -159
  20. package/dist/theme-reference.cjs +1 -1
  21. package/dist/theme-reference.d.cts +14 -0
  22. package/dist/theme-reference.d.ts +14 -0
  23. package/dist/widget.css +432 -0
  24. package/package.json +1 -1
  25. package/src/client.test.ts +134 -0
  26. package/src/client.ts +71 -0
  27. package/src/components/ask-user-question-bubble.test.ts +583 -0
  28. package/src/components/ask-user-question-bubble.ts +924 -0
  29. package/src/components/messages.ts +33 -1
  30. package/src/components/panel.ts +41 -4
  31. package/src/defaults.ts +21 -0
  32. package/src/index.ts +16 -1
  33. package/src/plugins/types.ts +57 -0
  34. package/src/session.test.ts +183 -0
  35. package/src/session.ts +242 -3
  36. package/src/styles/widget.css +432 -0
  37. package/src/types/theme.ts +15 -0
  38. package/src/types.ts +150 -0
  39. package/src/ui.ask-user-question-plugin.test.ts +649 -0
  40. package/src/ui.ts +631 -5
  41. package/src/utils/storage.ts +10 -2
  42. package/src/utils/theme.test.ts +36 -0
  43. package/src/utils/tokens.ts +23 -0
package/README.md CHANGED
@@ -939,6 +939,148 @@ Indicators are resolved in this order:
939
939
  2. **Config function** (`loadingIndicator.render` / `loadingIndicator.renderIdle`)
940
940
  3. **Default** (3-dot bouncing animation for loading, `null` for idle)
941
941
 
942
+ ### Ask User Question
943
+
944
+ The `ask_user_question` feature turns a LOCAL agent tool into an interactive prompt with tappable option pills. When the agent calls the `ask_user_question` tool, the server pauses execution and emits a `step_await` event; the widget renders an answer-pill sheet over the composer; the user picks / types / dismisses; the widget POSTs the answer to `/v1/dispatch/resume` and the paused execution continues with a structured `tool_result`.
945
+
946
+ This is the recommended pattern for human-in-the-loop clarifying questions. The agent-side setup (declare `ask_user_question` as a `runtimeTools` LOCAL tool and instruct the model to call it) lives in your `RuntypeFlowConfig` in the proxy — pair it with a `POST` handler that forwards to the upstream `/resume` endpoint (see `@runtypelabs/persona-proxy` and your deployment’s `resume` route).
947
+
948
+ #### Configuration
949
+
950
+ ```ts
951
+ features: {
952
+ askUserQuestion: {
953
+ enabled: true, // default: true. When false, the tool falls through to the normal tool-bubble path.
954
+ dismissible: true, // default: true. Shows a × close button on the sheet.
955
+ slideInMs: 180, // slide-in animation duration.
956
+ freeTextLabel: 'Other…',
957
+ freeTextPlaceholder: 'Type your answer…',
958
+ submitLabel: 'Send',
959
+ styles: {
960
+ sheetBackground: '#ffffff',
961
+ sheetBorder: '#e5e7eb',
962
+ sheetShadow: '0 12px 28px -10px rgba(0,0,0,0.15)',
963
+ pillBackground: 'transparent',
964
+ pillBackgroundSelected: '#0f0f0f',
965
+ pillTextColor: '#1f2937',
966
+ pillTextColorSelected: '#fafafa',
967
+ pillBorderRadius: '999px',
968
+ customInputBackground: '#ffffff'
969
+ }
970
+ }
971
+ }
972
+ ```
973
+
974
+ The composer-overlay sheet is the only question UI — no transcript stub is rendered. After the user picks, the picked answer appears as a normal user bubble so the transcript reads naturally.
975
+
976
+ #### DOM events
977
+
978
+ The widget dispatches two events on the mount element so the host page can react without touching the plugin API:
979
+
980
+ | Event | Detail |
981
+ |---|---|
982
+ | `persona:askUserQuestion:answered` | `{ toolUseId, answer, values, isFreeText, source }` where `source` is `'pick' \| 'multi' \| 'free-text'` |
983
+ | `persona:askUserQuestion:dismissed` | `{ toolUseId }` |
984
+
985
+ ```ts
986
+ mount.addEventListener('persona:askUserQuestion:answered', (event) => {
987
+ const { answer, source } = event.detail;
988
+ console.log('User picked', answer, 'via', source);
989
+ });
990
+ ```
991
+
992
+ #### Custom UI via the `renderAskUserQuestion` plugin hook
993
+
994
+ For full control over the question UI — a modal, a sidebar form, a command palette, whatever — register a plugin with `renderAskUserQuestion`. Returning a non-null `HTMLElement` renders inline in the transcript and suppresses the built-in overlay sheet. Returning `null` falls through to the default sheet.
995
+
996
+ ```ts
997
+ import type { AgentWidgetPlugin } from '@runtypelabs/persona';
998
+
999
+ const customAskPlugin: AgentWidgetPlugin = {
1000
+ id: 'custom-ask',
1001
+ renderAskUserQuestion: ({ payload, complete, resolve, dismiss }) => {
1002
+ const prompt = payload?.questions?.[0];
1003
+ if (!prompt) return null; // streaming — wait for more data, or show a skeleton
1004
+
1005
+ const root = document.createElement('div');
1006
+ root.className = 'my-question-card';
1007
+
1008
+ const q = document.createElement('p');
1009
+ q.textContent = prompt.question ?? '';
1010
+ root.appendChild(q);
1011
+
1012
+ (prompt.options ?? []).forEach((option) => {
1013
+ const btn = document.createElement('button');
1014
+ btn.textContent = option.label;
1015
+ btn.addEventListener('click', () => resolve(option.label));
1016
+ root.appendChild(btn);
1017
+ });
1018
+
1019
+ if (prompt.allowFreeText !== false) {
1020
+ const input = document.createElement('input');
1021
+ input.placeholder = 'Other…';
1022
+ input.addEventListener('keydown', (e) => {
1023
+ if (e.key === 'Enter' && input.value.trim()) resolve(input.value.trim());
1024
+ });
1025
+ root.appendChild(input);
1026
+ }
1027
+
1028
+ const close = document.createElement('button');
1029
+ close.textContent = '×';
1030
+ close.addEventListener('click', () => dismiss());
1031
+ root.appendChild(close);
1032
+
1033
+ return root;
1034
+ }
1035
+ };
1036
+
1037
+ initAgentWidget({
1038
+ target: '#app',
1039
+ config: {
1040
+ plugins: [customAskPlugin]
1041
+ }
1042
+ });
1043
+ ```
1044
+
1045
+ #### Type Definitions
1046
+
1047
+ ```ts
1048
+ type AskUserQuestionOption = {
1049
+ label: string;
1050
+ description?: string;
1051
+ };
1052
+
1053
+ type AskUserQuestionPrompt = {
1054
+ question: string;
1055
+ header?: string; // short chip label, ≤12 chars
1056
+ options: AskUserQuestionOption[];
1057
+ multiSelect?: boolean; // allow multiple picks with a Submit button
1058
+ allowFreeText?: boolean; // show an "Other…" free-text pill
1059
+ };
1060
+
1061
+ type AskUserQuestionPayload = {
1062
+ questions: AskUserQuestionPrompt[];
1063
+ };
1064
+
1065
+ // Plugin hook signature
1066
+ renderAskUserQuestion?: (context: {
1067
+ message: AgentWidgetMessage;
1068
+ payload: Partial<AskUserQuestionPayload> | null; // may be partial mid-stream
1069
+ complete: boolean; // true once tool-call args fully stream
1070
+ resolve: (answer: string) => void; // posts /resume with structured toolOutput
1071
+ dismiss: () => void; // sends "(dismissed)" sentinel
1072
+ config: AgentWidgetConfig;
1073
+ }) => HTMLElement | null;
1074
+ ```
1075
+
1076
+ For plugins that want to re-parse a tool message outside the hook context, the widget also exports a `parseAskUserQuestionPayload(message)` helper that returns `{ payload, complete }` using the same partial-JSON logic the built-in sheet uses.
1077
+
1078
+ #### Priority chain
1079
+
1080
+ 1. **Plugin hook** (`renderAskUserQuestion` returning a non-null element) — fully owns the UI; built-in overlay is suppressed.
1081
+ 2. **Built-in overlay sheet** — when the feature is enabled and no plugin handles it.
1082
+ 3. **Generic tool bubble** — when `features.askUserQuestion.enabled` is `false`, the tool call renders through the normal `renderToolCall` path.
1083
+
942
1084
  ### Dropdown Menu
943
1085
 
944
1086
  A reusable dropdown menu utility for building custom menus in plugins, custom components, or host-page UI that matches the widget's theme.
@@ -1,4 +1,4 @@
1
- import { S as StreamAnimationPlugin } from './types-HPZY7oAI.cjs';
1
+ import { S as StreamAnimationPlugin } from './types-cwY5HaFD.cjs';
2
2
 
3
3
  declare const glyphCycle: StreamAnimationPlugin;
4
4
 
@@ -1,4 +1,4 @@
1
- import { S as StreamAnimationPlugin } from './types-HPZY7oAI.js';
1
+ import { S as StreamAnimationPlugin } from './types-cwY5HaFD.js';
2
2
 
3
3
  declare const glyphCycle: StreamAnimationPlugin;
4
4
 
@@ -48,6 +48,31 @@ type AgentMessageMetadata = {
48
48
  * or `prompt` step inside the nested flow). Stable key for that step.
49
49
  */
50
50
  parentStepId?: string;
51
+ /**
52
+ * Set to `true` on a tool-variant message produced from a `step_await`
53
+ * event (`awaitReason: "local_tool_required"`). Signals to UI code that
54
+ * the tool call is a LOCAL tool and the server is paused waiting for a
55
+ * `POST /v1/dispatch/resume` with the user's answer keyed by tool name.
56
+ */
57
+ awaitingLocalTool?: boolean;
58
+ /**
59
+ * Set to `true` once the user has picked / typed / dismissed an answer for
60
+ * an `ask_user_question` tool call, so renderers stop re-mounting the
61
+ * answer-pill sheet for this tool call on subsequent render passes.
62
+ */
63
+ askUserQuestionAnswered?: boolean;
64
+ /**
65
+ * In-progress answers for a multi-question `ask_user_question` payload,
66
+ * keyed by question text. Persisted across refresh so the user lands back
67
+ * where they were if the page reloads mid-flow. Cleared once
68
+ * `askUserQuestionAnswered` flips to `true`.
69
+ */
70
+ askUserQuestionAnswers?: Record<string, string | string[]>;
71
+ /**
72
+ * Current page index for a multi-question `ask_user_question` payload's
73
+ * paginated stepper. Persists alongside `askUserQuestionAnswers`.
74
+ */
75
+ askUserQuestionIndex?: number;
51
76
  };
52
77
  /**
53
78
  * Context passed to plugin lifecycle hooks. Carries the live DOM references
@@ -48,6 +48,31 @@ type AgentMessageMetadata = {
48
48
  * or `prompt` step inside the nested flow). Stable key for that step.
49
49
  */
50
50
  parentStepId?: string;
51
+ /**
52
+ * Set to `true` on a tool-variant message produced from a `step_await`
53
+ * event (`awaitReason: "local_tool_required"`). Signals to UI code that
54
+ * the tool call is a LOCAL tool and the server is paused waiting for a
55
+ * `POST /v1/dispatch/resume` with the user's answer keyed by tool name.
56
+ */
57
+ awaitingLocalTool?: boolean;
58
+ /**
59
+ * Set to `true` once the user has picked / typed / dismissed an answer for
60
+ * an `ask_user_question` tool call, so renderers stop re-mounting the
61
+ * answer-pill sheet for this tool call on subsequent render passes.
62
+ */
63
+ askUserQuestionAnswered?: boolean;
64
+ /**
65
+ * In-progress answers for a multi-question `ask_user_question` payload,
66
+ * keyed by question text. Persisted across refresh so the user lands back
67
+ * where they were if the page reloads mid-flow. Cleared once
68
+ * `askUserQuestionAnswered` flips to `true`.
69
+ */
70
+ askUserQuestionAnswers?: Record<string, string | string[]>;
71
+ /**
72
+ * Current page index for a multi-question `ask_user_question` payload's
73
+ * paginated stepper. Persists alongside `askUserQuestionAnswers`.
74
+ */
75
+ askUserQuestionIndex?: number;
51
76
  };
52
77
  /**
53
78
  * Context passed to plugin lifecycle hooks. Carries the live DOM references
@@ -1,4 +1,4 @@
1
- import { S as StreamAnimationPlugin } from './types-HPZY7oAI.cjs';
1
+ import { S as StreamAnimationPlugin } from './types-cwY5HaFD.cjs';
2
2
 
3
3
  declare const wipe: StreamAnimationPlugin;
4
4
 
@@ -1,4 +1,4 @@
1
- import { S as StreamAnimationPlugin } from './types-HPZY7oAI.js';
1
+ import { S as StreamAnimationPlugin } from './types-cwY5HaFD.js';
2
2
 
3
3
  declare const wipe: StreamAnimationPlugin;
4
4