@runtypelabs/persona 3.9.0 → 3.9.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.
- package/dist/index.cjs +8 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.global.js +10 -10
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/theme-editor.cjs +12 -0
- package/dist/theme-editor.js +12 -0
- package/package.json +1 -1
- package/src/client.test.ts +54 -0
- package/src/client.ts +14 -0
package/dist/theme-editor.cjs
CHANGED
|
@@ -4842,6 +4842,18 @@ var AgentWidgetClient = class {
|
|
|
4842
4842
|
if (stepType === "tool" || executionType === "context") {
|
|
4843
4843
|
continue;
|
|
4844
4844
|
}
|
|
4845
|
+
if (didSplitByPartId) {
|
|
4846
|
+
if (assistantMessage !== null) {
|
|
4847
|
+
const msg = assistantMessage;
|
|
4848
|
+
streamParsers.delete(msg.id);
|
|
4849
|
+
rawContentBuffers.delete(msg.id);
|
|
4850
|
+
if (msg.streaming !== false) {
|
|
4851
|
+
msg.streaming = false;
|
|
4852
|
+
emitMessage(msg);
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
continue;
|
|
4856
|
+
}
|
|
4845
4857
|
const finalContent = (_ja = payload.result) == null ? void 0 : _ja.response;
|
|
4846
4858
|
const assistant = ensureAssistantMessage();
|
|
4847
4859
|
if (finalContent !== void 0 && finalContent !== null) {
|
package/dist/theme-editor.js
CHANGED
|
@@ -4737,6 +4737,18 @@ var AgentWidgetClient = class {
|
|
|
4737
4737
|
if (stepType === "tool" || executionType === "context") {
|
|
4738
4738
|
continue;
|
|
4739
4739
|
}
|
|
4740
|
+
if (didSplitByPartId) {
|
|
4741
|
+
if (assistantMessage !== null) {
|
|
4742
|
+
const msg = assistantMessage;
|
|
4743
|
+
streamParsers.delete(msg.id);
|
|
4744
|
+
rawContentBuffers.delete(msg.id);
|
|
4745
|
+
if (msg.streaming !== false) {
|
|
4746
|
+
msg.streaming = false;
|
|
4747
|
+
emitMessage(msg);
|
|
4748
|
+
}
|
|
4749
|
+
}
|
|
4750
|
+
continue;
|
|
4751
|
+
}
|
|
4740
4752
|
const finalContent = (_ja = payload.result) == null ? void 0 : _ja.response;
|
|
4741
4753
|
const assistant = ensureAssistantMessage();
|
|
4742
4754
|
if (finalContent !== void 0 && finalContent !== null) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtypelabs/persona",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.1",
|
|
4
4
|
"description": "Themeable, pluggable streaming agent widget for websites, in plain JS with support for voice input and reasoning / tool output.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
package/src/client.test.ts
CHANGED
|
@@ -1784,5 +1784,59 @@ describe('AgentWidgetClient - partId Text/Tool Interleaving', () => {
|
|
|
1784
1784
|
expect(assistantTexts[0].streaming).toBe(false);
|
|
1785
1785
|
expect(assistantTexts[1].streaming).toBe(false);
|
|
1786
1786
|
});
|
|
1787
|
+
|
|
1788
|
+
it('should not duplicate text when step_complete follows text_end', async () => {
|
|
1789
|
+
const events: AgentWidgetEvent[] = [];
|
|
1790
|
+
|
|
1791
|
+
const encoder = new TextEncoder();
|
|
1792
|
+
global.fetch = vi.fn().mockImplementation(async () => {
|
|
1793
|
+
const stream = new ReadableStream({
|
|
1794
|
+
start(controller) {
|
|
1795
|
+
const e = (eventType: string, data: Record<string, unknown>) =>
|
|
1796
|
+
controller.enqueue(encoder.encode(`event: ${eventType}\ndata: ${JSON.stringify({ type: eventType, ...data })}\n\n`));
|
|
1797
|
+
|
|
1798
|
+
e('flow_start', { flowId: 'f1', flowName: 'Test', totalSteps: 1 });
|
|
1799
|
+
// Tools fire first (no text before them)
|
|
1800
|
+
e('tool_start', { toolId: 'tc_1', name: 'test_tool', toolType: 'custom', startedAt: new Date().toISOString() });
|
|
1801
|
+
e('tool_complete', { toolId: 'tc_1', name: 'test_tool', success: true, completedAt: new Date().toISOString(), executionTime: 0 });
|
|
1802
|
+
// Then text segment
|
|
1803
|
+
e('text_start', { partId: 'text_1', messageId: 'msg_s1', seq: 1 });
|
|
1804
|
+
e('step_delta', { id: 's1', text: 'Tool returned a result.', partId: 'text_1', messageId: 'msg_s1', seq: 2 });
|
|
1805
|
+
e('text_end', { partId: 'text_1', messageId: 'msg_s1', seq: 3 });
|
|
1806
|
+
// step_complete with full response (should NOT create a duplicate)
|
|
1807
|
+
e('step_complete', { id: 's1', name: 'Response', stepType: 'prompt', success: true, result: { response: 'Tool returned a result.' }, executionTime: 500 });
|
|
1808
|
+
e('flow_complete', { success: true });
|
|
1809
|
+
controller.close();
|
|
1810
|
+
}
|
|
1811
|
+
});
|
|
1812
|
+
return { ok: true, body: stream };
|
|
1813
|
+
});
|
|
1814
|
+
|
|
1815
|
+
const client = new AgentWidgetClient({ apiUrl: 'http://localhost:8000' });
|
|
1816
|
+
await client.dispatch(
|
|
1817
|
+
{ messages: [{ id: 'usr_1', role: 'user', content: 'Call tool', createdAt: new Date().toISOString() }] },
|
|
1818
|
+
(event) => events.push(event)
|
|
1819
|
+
);
|
|
1820
|
+
|
|
1821
|
+
const messageEvents = events.filter(e => e.type === 'message');
|
|
1822
|
+
const messagesById = new Map<string, AgentWidgetMessage>();
|
|
1823
|
+
for (const event of messageEvents) {
|
|
1824
|
+
if (event.type === 'message') messagesById.set(event.message.id, event.message);
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
const allMessages = Array.from(messagesById.values());
|
|
1828
|
+
const assistantTexts = allMessages
|
|
1829
|
+
.filter(m => m.role === 'assistant' && !m.variant)
|
|
1830
|
+
.sort((a, b) => (a.sequence ?? 0) - (b.sequence ?? 0));
|
|
1831
|
+
const toolMsgs = allMessages.filter(m => m.variant === 'tool');
|
|
1832
|
+
|
|
1833
|
+
// Exactly ONE text message (not duplicated by step_complete)
|
|
1834
|
+
expect(assistantTexts.length).toBe(1);
|
|
1835
|
+
expect(assistantTexts[0].content).toBe('Tool returned a result.');
|
|
1836
|
+
expect(assistantTexts[0].streaming).toBe(false);
|
|
1837
|
+
|
|
1838
|
+
// Tool message exists
|
|
1839
|
+
expect(toolMsgs.length).toBe(1);
|
|
1840
|
+
});
|
|
1787
1841
|
});
|
|
1788
1842
|
|
package/src/client.ts
CHANGED
|
@@ -1757,6 +1757,20 @@ export class AgentWidgetClient {
|
|
|
1757
1757
|
// Skip tool-related completions - they're handled by tool_complete
|
|
1758
1758
|
continue;
|
|
1759
1759
|
}
|
|
1760
|
+
if (didSplitByPartId) {
|
|
1761
|
+
// text_end already sealed the assistant message(s) — don't recreate
|
|
1762
|
+
// one from step_complete's full response (would cause duplication)
|
|
1763
|
+
if (assistantMessage !== null) {
|
|
1764
|
+
const msg: AgentWidgetMessage = assistantMessage;
|
|
1765
|
+
streamParsers.delete(msg.id);
|
|
1766
|
+
rawContentBuffers.delete(msg.id);
|
|
1767
|
+
if (msg.streaming !== false) {
|
|
1768
|
+
msg.streaming = false;
|
|
1769
|
+
emitMessage(msg);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
continue;
|
|
1773
|
+
}
|
|
1760
1774
|
const finalContent = payload.result?.response;
|
|
1761
1775
|
const assistant = ensureAssistantMessage();
|
|
1762
1776
|
if (finalContent !== undefined && finalContent !== null) {
|