@runtypelabs/persona 4.21.1 → 4.22.1

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 (129) hide show
  1. package/dist/animations/glyph-cycle.d.cts +1 -1
  2. package/dist/animations/glyph-cycle.d.ts +1 -1
  3. package/dist/animations/{types-B_JdVK9o.d.cts → types-9Vxm-PPz.d.cts} +136 -6
  4. package/dist/animations/{types-B_JdVK9o.d.ts → types-9Vxm-PPz.d.ts} +136 -6
  5. package/dist/animations/wipe.d.cts +1 -1
  6. package/dist/animations/wipe.d.ts +1 -1
  7. package/dist/animations-extra.d.cts +136 -6
  8. package/dist/animations-extra.d.ts +136 -6
  9. package/dist/{approval-ui-RNNBR5R3.js → approval-ui-K7R5Q5OC.js} +1 -1
  10. package/dist/approval-ui.cjs +2 -2
  11. package/dist/approval-ui.d.cts +221 -16
  12. package/dist/approval-ui.d.ts +221 -16
  13. package/dist/approval-ui.js +2 -2
  14. package/dist/artifacts-ui-F4N6VMKF.js +7 -0
  15. package/dist/artifacts-ui.cjs +1 -1
  16. package/dist/artifacts-ui.d.cts +221 -16
  17. package/dist/artifacts-ui.d.ts +221 -16
  18. package/dist/artifacts-ui.js +1 -1
  19. package/dist/chunk-NQLDCJAP.js +4 -0
  20. package/dist/codegen.cjs +1 -1
  21. package/dist/codegen.js +1 -1
  22. package/dist/context-mentions.d.cts +221 -16
  23. package/dist/context-mentions.d.ts +221 -16
  24. package/dist/event-stream-view-GL3ADRY2.js +1 -0
  25. package/dist/event-stream-view.cjs +1 -1
  26. package/dist/event-stream-view.d.cts +221 -16
  27. package/dist/event-stream-view.d.ts +221 -16
  28. package/dist/event-stream-view.js +1 -1
  29. package/dist/forms-ui.d.cts +234 -25
  30. package/dist/forms-ui.d.ts +234 -25
  31. package/dist/history-view.d.cts +136 -6
  32. package/dist/history-view.d.ts +136 -6
  33. package/dist/index.cjs +70 -70
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +302 -32
  36. package/dist/index.d.ts +302 -32
  37. package/dist/index.global.js +54 -54
  38. package/dist/index.global.js.map +1 -1
  39. package/dist/index.js +69 -69
  40. package/dist/index.js.map +1 -1
  41. package/dist/launcher.global.js +2 -2
  42. package/dist/launcher.global.js.map +1 -1
  43. package/dist/session-reconnect.d.cts +221 -16
  44. package/dist/session-reconnect.d.ts +221 -16
  45. package/dist/smart-dom-reader.d.cts +221 -16
  46. package/dist/smart-dom-reader.d.ts +221 -16
  47. package/dist/testing.cjs +3 -3
  48. package/dist/testing.d.cts +3 -4
  49. package/dist/testing.d.ts +3 -4
  50. package/dist/testing.js +3 -3
  51. package/dist/theme-editor-preview.cjs +73 -73
  52. package/dist/theme-editor-preview.d.cts +221 -16
  53. package/dist/theme-editor-preview.d.ts +221 -16
  54. package/dist/theme-editor-preview.js +51 -51
  55. package/dist/theme-editor.cjs +4 -4
  56. package/dist/theme-editor.d.cts +221 -16
  57. package/dist/theme-editor.d.ts +221 -16
  58. package/dist/theme-editor.js +2 -2
  59. package/dist/theme-reference.cjs +1 -1
  60. package/dist/theme-reference.d.cts +65 -1
  61. package/dist/theme-reference.d.ts +65 -1
  62. package/dist/theme-reference.js +1 -1
  63. package/dist/voice-runtime.d.cts +6 -2
  64. package/dist/voice-runtime.d.ts +6 -2
  65. package/dist/webmcp-polyfill.js +1 -4
  66. package/dist/webmcp-runtime-JI7T4QYF.js +6 -0
  67. package/dist/webmcp-runtime.cjs +2 -2
  68. package/dist/webmcp-runtime.d.cts +34 -2
  69. package/dist/webmcp-runtime.d.ts +34 -2
  70. package/dist/webmcp-runtime.js +2 -2
  71. package/package.json +2 -2
  72. package/src/ask-user-question-tool.ts +1 -1
  73. package/src/client-history-rest.test.ts +2 -1
  74. package/src/client-visitor-history.test.ts +20 -4
  75. package/src/client.composer-options.test.ts +1 -1
  76. package/src/client.interrupt.test.ts +2 -2
  77. package/src/client.test.ts +477 -915
  78. package/src/client.ts +134 -494
  79. package/src/client.unified-tools.test.ts +76 -0
  80. package/src/components/artifact-preview.ts +5 -0
  81. package/src/components/event-stream-badge-theming.test.ts +162 -0
  82. package/src/components/event-stream-view.test.ts +76 -76
  83. package/src/components/event-stream-view.ts +55 -20
  84. package/src/components/tool-bubble.test.ts +12 -0
  85. package/src/components/tool-bubble.ts +13 -2
  86. package/src/event-stream-view-bundle.test.ts +2 -2
  87. package/src/generated/runtype-openapi-contract.ts +137 -1
  88. package/src/index-core.ts +2 -0
  89. package/src/internal/runtype-history-provider.test.ts +2 -2
  90. package/src/runtime/init.registry.test.ts +56 -0
  91. package/src/runtime/init.ts +17 -0
  92. package/src/session-history.test.ts +1 -1
  93. package/src/session.quote.test.ts +1 -1
  94. package/src/session.resubmit-from.test.ts +1 -1
  95. package/src/session.test.ts +5 -5
  96. package/src/session.ts +95 -36
  97. package/src/suggest-replies-tool.ts +1 -1
  98. package/src/testing/mock-stream.test.ts +37 -16
  99. package/src/testing/mock-stream.ts +14 -9
  100. package/src/types/theme.ts +41 -1
  101. package/src/types.ts +57 -15
  102. package/src/ui.ask-user-question-plugin.test.ts +7 -7
  103. package/src/ui.composer-defer-one.test.ts +6 -34
  104. package/src/ui.composer-draft-persistence.test.ts +1 -1
  105. package/src/ui.composer-quote.test.ts +1 -1
  106. package/src/ui.message-history-actions.test.ts +1 -1
  107. package/src/ui.reasoning-display.test.ts +136 -2
  108. package/src/ui.streaming-coalescing.test.ts +10 -13
  109. package/src/ui.ts +101 -35
  110. package/src/ui.typing-indicator.test.ts +216 -0
  111. package/src/ui.voice-custom-provider.test.ts +211 -0
  112. package/src/utils/__snapshots__/tokens-parity.test.ts.snap +1740 -0
  113. package/src/utils/event-stream-buffer.test.ts +13 -13
  114. package/src/utils/event-stream-capture.test.ts +20 -20
  115. package/src/utils/formatting.ts +22 -0
  116. package/src/utils/theme.ts +16 -0
  117. package/src/utils/throughput-tracker.test.ts +100 -142
  118. package/src/utils/throughput-tracker.ts +18 -132
  119. package/src/utils/tokens.ts +59 -0
  120. package/src/utils/voice-support.ts +18 -1
  121. package/src/voice/voice-turn-tracker.ts +18 -0
  122. package/src/webmcp-bridge.test.ts +132 -23
  123. package/src/webmcp-bridge.ts +27 -3
  124. package/src/webmcp-runtime-entry.ts +71 -11
  125. package/dist/artifacts-ui-J6AKUGTS.js +0 -7
  126. package/dist/chunk-HBE7LFNT.js +0 -4
  127. package/dist/event-stream-view-73NWKWVQ.js +0 -1
  128. package/dist/webmcp-runtime-P3CHZRHF.js +0 -6
  129. package/src/utils/__fixtures__/unified-translator.oracle.ts +0 -717
@@ -0,0 +1,76 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { AgentWidgetClient } from "./client";
3
+ import type { AgentWidgetEvent, AgentWidgetMessage } from "./types";
4
+ import type { RuntypeExecutionStreamEvent } from "./generated/runtype-openapi-contract";
5
+
6
+ type Frame = RuntypeExecutionStreamEvent extends infer E
7
+ ? E extends RuntypeExecutionStreamEvent ? Omit<E, "seq" | "executionId"> : never
8
+ : never;
9
+
10
+ async function consume(frames: Frame[]) {
11
+ const events: AgentWidgetEvent[] = [];
12
+ const client = new AgentWidgetClient({
13
+ apiUrl: "https://example.test/dispatch",
14
+ customFetch: async () => new Response(frames.map((frame, seq) =>
15
+ `event: ${frame.type}\ndata: ${JSON.stringify({ executionId: "exec_test", seq, ...frame })}\n\n`
16
+ ).join(""), { headers: { "Content-Type": "text/event-stream" } }),
17
+ });
18
+ await client.dispatch({ messages: [] }, event => events.push(event));
19
+ const messages = new Map<string, AgentWidgetMessage>();
20
+ for (const event of events) if (event.type === "message") messages.set(event.message.id, event.message);
21
+ return { events, messages: [...messages.values()] };
22
+ }
23
+
24
+ const start = (id: string, parameters?: Record<string, unknown>): Frame => ({
25
+ type: "tool_start", toolCallId: id, toolName: "lookup", toolType: "builtin", parameters,
26
+ });
27
+
28
+ describe("canonical unified tool channels", () => {
29
+ it.each([undefined, { query: "provisional" }])("applies final parameters after a provisional start (%j)", async parameters => {
30
+ const { messages } = await consume([
31
+ start("call_a", parameters),
32
+ { type: "tool_input_delta", toolCallId: "call_a", delta: '{"query":"hello"}' },
33
+ { type: "tool_input_complete", toolCallId: "call_a", parameters: { query: "hello" } },
34
+ { type: "tool_complete", toolCallId: "call_a", success: true },
35
+ ]);
36
+ expect(messages[0].toolCall).toMatchObject({ id: "call_a", args: { query: "hello" }, chunks: ['{"query":"hello"}'] });
37
+ });
38
+
39
+ it("accepts empty final parameters", async () => {
40
+ const { messages } = await consume([start("call_a", { stale: true }),
41
+ { type: "tool_input_complete", toolCallId: "call_a", parameters: {} }]);
42
+ expect(messages[0].toolCall?.args).toEqual({});
43
+ });
44
+
45
+ it("keeps continuation calls separate from the last started call", async () => {
46
+ const { messages } = await consume([
47
+ start("call_b"),
48
+ { type: "tool_input_complete", toolCallId: "call_a", toolName: "lookup", parameters: { query: "A" } },
49
+ { type: "tool_output_delta", toolCallId: "call_a", delta: "A output" },
50
+ { type: "tool_complete", toolCallId: "call_a", toolName: "lookup", success: true, result: "A result", executionTime: 42 },
51
+ { type: "tool_complete", toolCallId: "call_b", success: true, result: "B result" },
52
+ ]);
53
+ expect(messages.map(m => m.toolCall?.id).sort()).toEqual(["call_a", "call_b"]);
54
+ expect(messages.find(m => m.toolCall?.id === "call_a")?.toolCall).toMatchObject({ args: { query: "A" }, chunks: ["A output"], result: "A result", duration: 42, durationMs: 42 });
55
+ expect(messages.find(m => m.toolCall?.id === "call_b")?.toolCall?.result).toBe("B result");
56
+ });
57
+
58
+ it("keeps the canonical ID and name on a completion-only continuation", async () => {
59
+ const { messages } = await consume([{ type: "tool_complete", toolCallId: "call_a", toolName: "lookup", success: true }]);
60
+ expect(messages[0].toolCall).toMatchObject({ id: "call_a", name: "lookup" });
61
+ });
62
+
63
+ it("retains a tool failure while the execution continues", async () => {
64
+ const { messages, events } = await consume([
65
+ start("call_a"),
66
+ { type: "tool_complete", toolCallId: "call_a", success: false, error: "Search timed out" },
67
+ { type: "text_start", id: "text_a" },
68
+ { type: "text_delta", id: "text_a", delta: "I can try another search." },
69
+ { type: "text_complete", id: "text_a" },
70
+ { type: "execution_complete", kind: "agent", success: true },
71
+ ]);
72
+ expect(messages[0].toolCall).toMatchObject({ status: "complete", success: false, error: "Search timed out" });
73
+ expect(messages.at(-1)?.content).toBe("I can try another search.");
74
+ expect(events.filter(e => e.type === "error")).toEqual([]);
75
+ });
76
+ });
@@ -447,6 +447,11 @@ function setupPreviewLoading(
447
447
  const addTimer = (fn: () => void, ms: number): void => {
448
448
  const id = setTimeout(() => {
449
449
  timers.delete(id);
450
+ // Unit tests drop rendered blocks without running destroy(), so a
451
+ // pending timer can outlive the test file's DOM environment. Every
452
+ // callback here does DOM work; with no document left there is nothing
453
+ // to do (and `document.createElement` would throw).
454
+ if (typeof document === "undefined") return;
450
455
  fn();
451
456
  }, ms) as unknown as number;
452
457
  timers.add(id);
@@ -0,0 +1,162 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ DEFAULT_BADGE_COLORS,
4
+ getBadgeBorderColor,
5
+ getBadgeColor,
6
+ } from "./event-stream-view";
7
+ import { createDarkTheme, createLightTheme, themeToCssVariables } from "../utils/theme";
8
+
9
+ /**
10
+ * Contrast lock for the event-stream badge chips. The chip paints inline
11
+ * `var(--persona-event-badge-*)` styles, so the pairs the theme pipeline emits
12
+ * ARE the rendered colors inside any themed root; asserting on the emitted
13
+ * variables is equivalent to a rendered-computed-style check without a
14
+ * browser. Both schemes must hold every family at >= 4.5:1 (WCAG AA for the
15
+ * 11px badge text).
16
+ */
17
+
18
+ const FAMILIES = [
19
+ "flow",
20
+ "step",
21
+ "reasoning",
22
+ "tool",
23
+ "agent",
24
+ "error",
25
+ "default",
26
+ ] as const;
27
+
28
+ const HEX6 = /^#[0-9a-fA-F]{6}$/;
29
+
30
+ function channelLuminance(byte: number): number {
31
+ const c = byte / 255;
32
+ return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
33
+ }
34
+
35
+ function relativeLuminance(hex: string): number {
36
+ const r = channelLuminance(parseInt(hex.slice(1, 3), 16));
37
+ const g = channelLuminance(parseInt(hex.slice(3, 5), 16));
38
+ const b = channelLuminance(parseInt(hex.slice(5, 7), 16));
39
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
40
+ }
41
+
42
+ function contrastRatio(a: string, b: string): number {
43
+ const la = relativeLuminance(a);
44
+ const lb = relativeLuminance(b);
45
+ const [hi, lo] = la >= lb ? [la, lb] : [lb, la];
46
+ return (hi + 0.05) / (lo + 0.05);
47
+ }
48
+
49
+ const SCHEMES = {
50
+ light: () => themeToCssVariables(createLightTheme()),
51
+ dark: () => themeToCssVariables(createDarkTheme()),
52
+ } as const;
53
+
54
+ describe("event-stream badge theming", () => {
55
+ for (const [scheme, buildVars] of Object.entries(SCHEMES)) {
56
+ it(`${scheme} scheme emits a >=4.5:1 pair for every badge family`, () => {
57
+ const vars = buildVars();
58
+ for (const family of FAMILIES) {
59
+ const bg = vars[`--persona-event-badge-${family}-bg`];
60
+ const fg = vars[`--persona-event-badge-${family}-fg`];
61
+ expect(bg, `${family} bg`).toMatch(HEX6);
62
+ expect(fg, `${family} fg`).toMatch(HEX6);
63
+ const ratio = contrastRatio(bg, fg);
64
+ expect(
65
+ ratio,
66
+ `${scheme} ${family}: ${fg} on ${bg} is ${ratio.toFixed(2)}:1`
67
+ ).toBeGreaterThanOrEqual(4.5);
68
+ }
69
+ });
70
+ }
71
+
72
+ it("light pairs keep the pre-token rendered colors", () => {
73
+ // The 4.x chips resolved these exact values inside a themed root; the
74
+ // token pipeline must reproduce them so light mode is unchanged.
75
+ const vars = SCHEMES.light();
76
+ expect(vars["--persona-event-badge-flow-bg"]).toBe("#dcfce7");
77
+ expect(vars["--persona-event-badge-flow-fg"]).toBe("#15803d");
78
+ expect(vars["--persona-event-badge-tool-bg"]).toBe("#f3e8ff");
79
+ expect(vars["--persona-event-badge-tool-fg"]).toBe("#6b21a8");
80
+ expect(vars["--persona-event-badge-agent-bg"]).toBe("#ccfbf1");
81
+ expect(vars["--persona-event-badge-agent-fg"]).toBe("#115e59");
82
+ expect(vars["--persona-event-badge-default-bg"]).toBe("#f3f4f6");
83
+ expect(vars["--persona-event-badge-default-fg"]).toBe("#4b5563");
84
+ });
85
+
86
+ it("dark pairs differ from light for every family", () => {
87
+ const light = SCHEMES.light();
88
+ const dark = SCHEMES.dark();
89
+ for (const family of FAMILIES) {
90
+ expect(
91
+ dark[`--persona-event-badge-${family}-bg`],
92
+ `${family} bg`
93
+ ).not.toBe(light[`--persona-event-badge-${family}-bg`]);
94
+ }
95
+ });
96
+
97
+ it("every default badge value references a variable both schemes emit", () => {
98
+ const light = SCHEMES.light();
99
+ const dark = SCHEMES.dark();
100
+ const pairs = [...Object.values(DEFAULT_BADGE_COLORS), getBadgeColor("unmatched_type")];
101
+ for (const pair of pairs) {
102
+ for (const value of [pair.bg, pair.text]) {
103
+ const match = /^var\((--persona-event-badge-[a-z]+-(?:bg|fg)), #[0-9a-f]{6}\)$/.exec(
104
+ value
105
+ );
106
+ expect(match, value).not.toBeNull();
107
+ const varName = match![1];
108
+ expect(light[varName], `${varName} (light)`).toBeDefined();
109
+ expect(dark[varName], `${varName} (dark)`).toBeDefined();
110
+ }
111
+ }
112
+ });
113
+
114
+ it("routes event types to families: prefixes, reasoning, and execution_error", () => {
115
+ expect(getBadgeColor("execution_start")).toBe(DEFAULT_BADGE_COLORS.flow_);
116
+ expect(getBadgeColor("turn_start")).toBe(DEFAULT_BADGE_COLORS.agent_);
117
+ expect(getBadgeColor("text_delta")).toBe(DEFAULT_BADGE_COLORS.agent_);
118
+ expect(getBadgeColor("flow_start")).toBe(DEFAULT_BADGE_COLORS.flow_);
119
+ expect(getBadgeColor("tool_input_delta")).toBe(DEFAULT_BADGE_COLORS.tool_);
120
+ expect(getBadgeColor("agent_turn_start")).toBe(DEFAULT_BADGE_COLORS.agent_);
121
+ // reasoning_* must hit the reasoning family (the old `reason_` prefix
122
+ // never matched `reasoning_start` and dropped it to the default pair).
123
+ expect(getBadgeColor("reasoning_start")).toBe(DEFAULT_BADGE_COLORS.reasoning_);
124
+ expect(getBadgeColor("error")).toBe(DEFAULT_BADGE_COLORS.error);
125
+ expect(getBadgeColor("execution_error")).toBe(DEFAULT_BADGE_COLORS.error);
126
+ });
127
+
128
+ it("custom badgeColors: exact match beats prefix match", () => {
129
+ const exact = { bg: "#111111", text: "#eeeeee" };
130
+ const prefix = { bg: "#222222", text: "#dddddd" };
131
+ const custom = { tool_: prefix, ["tool_start"]: exact };
132
+ expect(getBadgeColor("tool_start", custom)).toBe(exact);
133
+ expect(getBadgeColor("tool_complete", custom)).toBe(prefix);
134
+ });
135
+
136
+ it("custom badgeColors: any custom match beats any default match", () => {
137
+ const custom = { execution_: { bg: "#222222", text: "#dddddd" } };
138
+ // A host prefix must cover its whole family: the built-in exact
139
+ // `execution_error` entry sits in the lower default tier.
140
+ expect(getBadgeColor("execution_error", custom)).toBe(custom.execution_);
141
+ expect(getBadgeColor("execution_start", custom)).toBe(custom.execution_);
142
+ // A narrower custom prefix wins over the default family prefix (the old
143
+ // merged-map lookup iterated the default `tool_` key first).
144
+ const narrowPair = { bg: "#333333", text: "#cccccc" };
145
+ expect(getBadgeColor("tool_input_delta", { ["tool_input_"]: narrowPair })).toBe(narrowPair);
146
+ // Unmatched custom maps still fall through to the default families.
147
+ expect(getBadgeColor("flow_start", custom)).toBe(DEFAULT_BADGE_COLORS.flow_);
148
+ });
149
+
150
+ it("border composition is valid CSS for hex and non-hex text colors", () => {
151
+ // Bare 6-digit hex keeps the historical alpha suffix.
152
+ expect(getBadgeBorderColor("#6b21a8")).toBe("#6b21a850");
153
+ // var() refs (every default) and other CSS colors must compose via
154
+ // color-mix; a bare suffix would produce an invalid, dropped declaration.
155
+ expect(getBadgeBorderColor("var(--persona-event-badge-tool-fg, #6b21a8)")).toBe(
156
+ "color-mix(in srgb, var(--persona-event-badge-tool-fg, #6b21a8) 31%, transparent)"
157
+ );
158
+ expect(getBadgeBorderColor("rgb(107, 33, 168)")).toBe(
159
+ "color-mix(in srgb, rgb(107, 33, 168) 31%, transparent)"
160
+ );
161
+ });
162
+ });