@runtypelabs/persona 3.17.0 → 3.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -1
- package/dist/animations/glyph-cycle.d.cts +1 -1
- package/dist/animations/glyph-cycle.d.ts +1 -1
- package/dist/animations/{types-HPZY7oAI.d.cts → types-cwY5HaFD.d.cts} +25 -0
- package/dist/animations/{types-HPZY7oAI.d.ts → types-cwY5HaFD.d.ts} +25 -0
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/index.cjs +47 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +580 -4
- package/dist/index.d.ts +580 -4
- package/dist/index.global.js +102 -1636
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +45 -45
- package/dist/index.js.map +1 -1
- package/dist/theme-editor.cjs +2844 -752
- package/dist/theme-editor.d.cts +337 -1
- package/dist/theme-editor.d.ts +337 -1
- package/dist/theme-editor.js +2958 -752
- package/dist/theme-reference.cjs +1 -1
- package/dist/theme-reference.d.cts +14 -0
- package/dist/theme-reference.d.ts +14 -0
- package/dist/widget.css +780 -0
- package/package.json +1 -1
- package/src/client.test.ts +134 -0
- package/src/client.ts +71 -0
- package/src/components/ask-user-question-bubble.test.ts +583 -0
- package/src/components/ask-user-question-bubble.ts +924 -0
- package/src/components/composer-builder.test.ts +52 -0
- package/src/components/composer-builder.ts +67 -490
- package/src/components/composer-parts.test.ts +152 -0
- package/src/components/composer-parts.ts +452 -0
- package/src/components/header-builder.ts +22 -299
- package/src/components/header-parts.ts +360 -0
- package/src/components/messages.ts +33 -1
- package/src/components/panel.test.ts +61 -0
- package/src/components/panel.ts +303 -9
- package/src/components/pill-composer-builder.test.ts +85 -0
- package/src/components/pill-composer-builder.ts +183 -0
- package/src/defaults.ts +21 -0
- package/src/index.ts +20 -1
- package/src/plugins/types.ts +57 -0
- package/src/runtime/init.ts +4 -2
- package/src/runtime/persist-state.test.ts +152 -0
- package/src/session.test.ts +183 -0
- package/src/session.ts +242 -3
- package/src/styles/widget.css +780 -0
- package/src/types/theme.ts +15 -0
- package/src/types.ts +271 -1
- package/src/ui.ask-user-question-plugin.test.ts +649 -0
- package/src/ui.component-directive.test.ts +183 -0
- package/src/ui.composer-bar.test.ts +1009 -0
- package/src/ui.ts +1439 -76
- package/src/utils/attachment-manager.ts +1 -1
- package/src/utils/dock.test.ts +45 -0
- package/src/utils/dock.ts +3 -0
- package/src/utils/icons.ts +314 -58
- package/src/utils/storage.ts +10 -2
- package/src/utils/stream-animation.ts +7 -2
- package/src/utils/theme.test.ts +36 -0
- 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.
|
|
@@ -1920,7 +2062,7 @@ In docked mode, `position`, `fullHeight`, and `sidebarMode` are ignored because
|
|
|
1920
2062
|
|
|
1921
2063
|
| Option | Type | Description |
|
|
1922
2064
|
| --- | --- | --- |
|
|
1923
|
-
| `components` | `Record<string, AgentWidgetComponentRenderer>` | Registry of custom components rendered from JSON directives (`{"component": "Name", "props": {...}}`). Each renderer receives `(props, context)` and returns an `HTMLElement`. |
|
|
2065
|
+
| `components` | `Record<string, AgentWidgetComponentRenderer>` | Registry of custom components rendered from JSON directives (`{"component": "Name", "props": {...}}`). Each renderer receives `(props, context)` and returns an `HTMLElement`. Event listeners attached via `addEventListener` (and any other imperative state on the returned element) are preserved across transcript updates — the widget injects the live element directly into the morphed wrapper so listeners survive subsequent re-renders. The renderer is re-invoked when the directive's props change; for state you want to persist across prop changes, hold it in a closure outside the render. |
|
|
1924
2066
|
|
|
1925
2067
|
```typescript
|
|
1926
2068
|
config: {
|
|
@@ -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
|