@runtypelabs/persona 3.21.0 → 3.21.2
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 +7 -1
- package/dist/index.cjs +31 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.global.js +45 -45
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +32 -32
- package/dist/index.js.map +1 -1
- package/dist/theme-editor.cjs +67 -30
- package/dist/theme-editor.js +67 -30
- package/package.json +1 -1
- package/src/client.test.ts +234 -0
- package/src/client.ts +29 -5
- package/src/session.test.ts +151 -0
- package/src/session.ts +78 -21
- package/src/ui.ask-user-question-plugin.test.ts +42 -0
package/dist/theme-editor.cjs
CHANGED
|
@@ -4644,6 +4644,7 @@ var AgentWidgetClient = class {
|
|
|
4644
4644
|
});
|
|
4645
4645
|
};
|
|
4646
4646
|
let assistantMessage = null;
|
|
4647
|
+
let lastAssistantInTurn = null;
|
|
4647
4648
|
const assistantMessageRef = { current: null };
|
|
4648
4649
|
const partIdState = { current: null };
|
|
4649
4650
|
let didSplitByPartId = false;
|
|
@@ -5640,6 +5641,7 @@ var AgentWidgetClient = class {
|
|
|
5640
5641
|
}
|
|
5641
5642
|
}
|
|
5642
5643
|
} else if (payloadType === "agent_turn_start") {
|
|
5644
|
+
lastAssistantInTurn = null;
|
|
5643
5645
|
} else if (payloadType === "agent_turn_delta") {
|
|
5644
5646
|
if (payload.contentType === "text") {
|
|
5645
5647
|
const assistant = ensureAssistantMessage();
|
|
@@ -5650,6 +5652,7 @@ var AgentWidgetClient = class {
|
|
|
5650
5652
|
turnId: payload.turnId,
|
|
5651
5653
|
agentName: agentExecution == null ? void 0 : agentExecution.agentName
|
|
5652
5654
|
};
|
|
5655
|
+
lastAssistantInTurn = assistant;
|
|
5653
5656
|
emitMessage(assistant);
|
|
5654
5657
|
} else if (payload.contentType === "thinking") {
|
|
5655
5658
|
const reasoningId = (_Ha = payload.turnId) != null ? _Ha : `agent-think-${payload.iteration}`;
|
|
@@ -5694,15 +5697,21 @@ var AgentWidgetClient = class {
|
|
|
5694
5697
|
}
|
|
5695
5698
|
}
|
|
5696
5699
|
const turnStopReason = payload.stopReason;
|
|
5697
|
-
|
|
5700
|
+
const stopReasonTarget = assistantMessage != null ? assistantMessage : lastAssistantInTurn;
|
|
5701
|
+
if (turnStopReason && stopReasonTarget !== null) {
|
|
5698
5702
|
const turnId = payload.turnId;
|
|
5699
|
-
const matchesTurn = !turnId || ((_Pa =
|
|
5703
|
+
const matchesTurn = !turnId || ((_Pa = stopReasonTarget.agentMetadata) == null ? void 0 : _Pa.turnId) === turnId;
|
|
5700
5704
|
if (matchesTurn) {
|
|
5701
|
-
|
|
5702
|
-
emitMessage(
|
|
5705
|
+
stopReasonTarget.stopReason = turnStopReason;
|
|
5706
|
+
emitMessage(stopReasonTarget);
|
|
5703
5707
|
}
|
|
5704
5708
|
}
|
|
5705
5709
|
} else if (payloadType === "agent_tool_start") {
|
|
5710
|
+
if (assistantMessage) {
|
|
5711
|
+
assistantMessage.streaming = false;
|
|
5712
|
+
emitMessage(assistantMessage);
|
|
5713
|
+
assistantMessage = null;
|
|
5714
|
+
}
|
|
5706
5715
|
const toolId = (_Qa = payload.toolCallId) != null ? _Qa : `agent-tool-${nextSequence()}`;
|
|
5707
5716
|
trackToolId(getToolCallKey(payload), toolId);
|
|
5708
5717
|
const toolMessage = ensureToolMessage(toolId);
|
|
@@ -7871,8 +7880,10 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
7871
7880
|
*/
|
|
7872
7881
|
async connectStream(stream, options) {
|
|
7873
7882
|
var _a, _b, _c;
|
|
7874
|
-
if (this.streaming) return;
|
|
7875
|
-
(
|
|
7883
|
+
if (this.streaming && !(options == null ? void 0 : options.allowReentry)) return;
|
|
7884
|
+
if (!(options == null ? void 0 : options.allowReentry)) {
|
|
7885
|
+
(_a = this.abortController) == null ? void 0 : _a.abort();
|
|
7886
|
+
}
|
|
7876
7887
|
let hasStale = false;
|
|
7877
7888
|
for (const msg of this.messages) {
|
|
7878
7889
|
if (msg.streaming) {
|
|
@@ -7906,7 +7917,7 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
7906
7917
|
* and pipes the response stream through connectStream().
|
|
7907
7918
|
*/
|
|
7908
7919
|
async resolveApproval(approval, decision) {
|
|
7909
|
-
var _a, _b, _c;
|
|
7920
|
+
var _a, _b, _c, _d;
|
|
7910
7921
|
const approvalMessageId = `approval-${approval.id}`;
|
|
7911
7922
|
const updatedApproval = {
|
|
7912
7923
|
...approval,
|
|
@@ -7923,6 +7934,9 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
7923
7934
|
approval: updatedApproval
|
|
7924
7935
|
};
|
|
7925
7936
|
this.upsertMessage(updatedMessage);
|
|
7937
|
+
(_a = this.abortController) == null ? void 0 : _a.abort();
|
|
7938
|
+
this.abortController = new AbortController();
|
|
7939
|
+
this.setStreaming(true);
|
|
7926
7940
|
const approvalConfig = this.config.approval;
|
|
7927
7941
|
const onDecision = approvalConfig && typeof approvalConfig === "object" ? approvalConfig.onDecision : void 0;
|
|
7928
7942
|
try {
|
|
@@ -7953,7 +7967,7 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
7953
7967
|
if (!response.ok) {
|
|
7954
7968
|
const errorData = await response.json().catch(() => null);
|
|
7955
7969
|
throw new Error(
|
|
7956
|
-
(
|
|
7970
|
+
(_b = errorData == null ? void 0 : errorData.error) != null ? _b : `Approval request failed: ${response.status}`
|
|
7957
7971
|
);
|
|
7958
7972
|
}
|
|
7959
7973
|
stream = response.body;
|
|
@@ -7961,23 +7975,35 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
7961
7975
|
stream = response;
|
|
7962
7976
|
}
|
|
7963
7977
|
if (stream) {
|
|
7964
|
-
await this.connectStream(stream);
|
|
7965
|
-
} else
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
|
|
7972
|
-
|
|
7973
|
-
|
|
7978
|
+
await this.connectStream(stream, { allowReentry: true });
|
|
7979
|
+
} else {
|
|
7980
|
+
if (decision === "denied") {
|
|
7981
|
+
this.appendMessage({
|
|
7982
|
+
id: `denial-${approval.id}`,
|
|
7983
|
+
role: "assistant",
|
|
7984
|
+
content: "Tool execution was denied by user.",
|
|
7985
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7986
|
+
streaming: false,
|
|
7987
|
+
sequence: this.nextSequence()
|
|
7988
|
+
});
|
|
7989
|
+
}
|
|
7990
|
+
this.setStreaming(false);
|
|
7991
|
+
this.abortController = null;
|
|
7974
7992
|
}
|
|
7993
|
+
} else {
|
|
7994
|
+
this.setStreaming(false);
|
|
7995
|
+
this.abortController = null;
|
|
7975
7996
|
}
|
|
7976
7997
|
} catch (error) {
|
|
7977
|
-
|
|
7978
|
-
|
|
7979
|
-
|
|
7980
|
-
)
|
|
7998
|
+
const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("abort"));
|
|
7999
|
+
this.setStreaming(false);
|
|
8000
|
+
this.abortController = null;
|
|
8001
|
+
if (!isAbortError) {
|
|
8002
|
+
(_d = (_c = this.callbacks).onError) == null ? void 0 : _d.call(
|
|
8003
|
+
_c,
|
|
8004
|
+
error instanceof Error ? error : new Error(String(error))
|
|
8005
|
+
);
|
|
8006
|
+
}
|
|
7981
8007
|
}
|
|
7982
8008
|
}
|
|
7983
8009
|
/**
|
|
@@ -8031,7 +8057,7 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
8031
8057
|
});
|
|
8032
8058
|
}
|
|
8033
8059
|
async resolveAskUserQuestion(toolMessage, answer) {
|
|
8034
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
8060
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
8035
8061
|
const live = this.messages.find((m) => m.id === toolMessage.id);
|
|
8036
8062
|
if (((_a = live == null ? void 0 : live.agentMetadata) == null ? void 0 : _a.askUserQuestionAnswered) === true) return;
|
|
8037
8063
|
const executionId = (_b = toolMessage.agentMetadata) == null ? void 0 : _b.executionId;
|
|
@@ -8055,8 +8081,11 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
8055
8081
|
}
|
|
8056
8082
|
}
|
|
8057
8083
|
this.markAskUserQuestionResolved(toolMessage, structuredAnswers);
|
|
8084
|
+
(_h = this.abortController) == null ? void 0 : _h.abort();
|
|
8085
|
+
this.abortController = new AbortController();
|
|
8086
|
+
this.setStreaming(true);
|
|
8058
8087
|
const toolCallId = toolMessage.toolCall.id;
|
|
8059
|
-
const args = (
|
|
8088
|
+
const args = (_i = toolMessage.toolCall) == null ? void 0 : _i.args;
|
|
8060
8089
|
const questions = Array.isArray(args == null ? void 0 : args.questions) ? args.questions : [];
|
|
8061
8090
|
if (questions.length === 0) {
|
|
8062
8091
|
const fallback = typeof answer === "string" ? answer : Object.entries(answer).map(
|
|
@@ -8102,17 +8131,25 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
8102
8131
|
if (!response.ok) {
|
|
8103
8132
|
const errorData = await response.json().catch(() => null);
|
|
8104
8133
|
throw new Error(
|
|
8105
|
-
(
|
|
8134
|
+
(_j = errorData == null ? void 0 : errorData.error) != null ? _j : `Resume failed: ${response.status}`
|
|
8106
8135
|
);
|
|
8107
8136
|
}
|
|
8108
8137
|
if (response.body) {
|
|
8109
|
-
await this.connectStream(response.body);
|
|
8138
|
+
await this.connectStream(response.body, { allowReentry: true });
|
|
8139
|
+
} else {
|
|
8140
|
+
this.setStreaming(false);
|
|
8141
|
+
this.abortController = null;
|
|
8110
8142
|
}
|
|
8111
8143
|
} catch (error) {
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
|
|
8115
|
-
)
|
|
8144
|
+
const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("abort"));
|
|
8145
|
+
this.setStreaming(false);
|
|
8146
|
+
this.abortController = null;
|
|
8147
|
+
if (!isAbortError) {
|
|
8148
|
+
(_l = (_k = this.callbacks).onError) == null ? void 0 : _l.call(
|
|
8149
|
+
_k,
|
|
8150
|
+
error instanceof Error ? error : new Error(String(error))
|
|
8151
|
+
);
|
|
8152
|
+
}
|
|
8116
8153
|
}
|
|
8117
8154
|
}
|
|
8118
8155
|
cancel() {
|
package/dist/theme-editor.js
CHANGED
|
@@ -4533,6 +4533,7 @@ var AgentWidgetClient = class {
|
|
|
4533
4533
|
});
|
|
4534
4534
|
};
|
|
4535
4535
|
let assistantMessage = null;
|
|
4536
|
+
let lastAssistantInTurn = null;
|
|
4536
4537
|
const assistantMessageRef = { current: null };
|
|
4537
4538
|
const partIdState = { current: null };
|
|
4538
4539
|
let didSplitByPartId = false;
|
|
@@ -5529,6 +5530,7 @@ var AgentWidgetClient = class {
|
|
|
5529
5530
|
}
|
|
5530
5531
|
}
|
|
5531
5532
|
} else if (payloadType === "agent_turn_start") {
|
|
5533
|
+
lastAssistantInTurn = null;
|
|
5532
5534
|
} else if (payloadType === "agent_turn_delta") {
|
|
5533
5535
|
if (payload.contentType === "text") {
|
|
5534
5536
|
const assistant = ensureAssistantMessage();
|
|
@@ -5539,6 +5541,7 @@ var AgentWidgetClient = class {
|
|
|
5539
5541
|
turnId: payload.turnId,
|
|
5540
5542
|
agentName: agentExecution == null ? void 0 : agentExecution.agentName
|
|
5541
5543
|
};
|
|
5544
|
+
lastAssistantInTurn = assistant;
|
|
5542
5545
|
emitMessage(assistant);
|
|
5543
5546
|
} else if (payload.contentType === "thinking") {
|
|
5544
5547
|
const reasoningId = (_Ha = payload.turnId) != null ? _Ha : `agent-think-${payload.iteration}`;
|
|
@@ -5583,15 +5586,21 @@ var AgentWidgetClient = class {
|
|
|
5583
5586
|
}
|
|
5584
5587
|
}
|
|
5585
5588
|
const turnStopReason = payload.stopReason;
|
|
5586
|
-
|
|
5589
|
+
const stopReasonTarget = assistantMessage != null ? assistantMessage : lastAssistantInTurn;
|
|
5590
|
+
if (turnStopReason && stopReasonTarget !== null) {
|
|
5587
5591
|
const turnId = payload.turnId;
|
|
5588
|
-
const matchesTurn = !turnId || ((_Pa =
|
|
5592
|
+
const matchesTurn = !turnId || ((_Pa = stopReasonTarget.agentMetadata) == null ? void 0 : _Pa.turnId) === turnId;
|
|
5589
5593
|
if (matchesTurn) {
|
|
5590
|
-
|
|
5591
|
-
emitMessage(
|
|
5594
|
+
stopReasonTarget.stopReason = turnStopReason;
|
|
5595
|
+
emitMessage(stopReasonTarget);
|
|
5592
5596
|
}
|
|
5593
5597
|
}
|
|
5594
5598
|
} else if (payloadType === "agent_tool_start") {
|
|
5599
|
+
if (assistantMessage) {
|
|
5600
|
+
assistantMessage.streaming = false;
|
|
5601
|
+
emitMessage(assistantMessage);
|
|
5602
|
+
assistantMessage = null;
|
|
5603
|
+
}
|
|
5595
5604
|
const toolId = (_Qa = payload.toolCallId) != null ? _Qa : `agent-tool-${nextSequence()}`;
|
|
5596
5605
|
trackToolId(getToolCallKey(payload), toolId);
|
|
5597
5606
|
const toolMessage = ensureToolMessage(toolId);
|
|
@@ -7760,8 +7769,10 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
7760
7769
|
*/
|
|
7761
7770
|
async connectStream(stream, options) {
|
|
7762
7771
|
var _a, _b, _c;
|
|
7763
|
-
if (this.streaming) return;
|
|
7764
|
-
(
|
|
7772
|
+
if (this.streaming && !(options == null ? void 0 : options.allowReentry)) return;
|
|
7773
|
+
if (!(options == null ? void 0 : options.allowReentry)) {
|
|
7774
|
+
(_a = this.abortController) == null ? void 0 : _a.abort();
|
|
7775
|
+
}
|
|
7765
7776
|
let hasStale = false;
|
|
7766
7777
|
for (const msg of this.messages) {
|
|
7767
7778
|
if (msg.streaming) {
|
|
@@ -7795,7 +7806,7 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
7795
7806
|
* and pipes the response stream through connectStream().
|
|
7796
7807
|
*/
|
|
7797
7808
|
async resolveApproval(approval, decision) {
|
|
7798
|
-
var _a, _b, _c;
|
|
7809
|
+
var _a, _b, _c, _d;
|
|
7799
7810
|
const approvalMessageId = `approval-${approval.id}`;
|
|
7800
7811
|
const updatedApproval = {
|
|
7801
7812
|
...approval,
|
|
@@ -7812,6 +7823,9 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
7812
7823
|
approval: updatedApproval
|
|
7813
7824
|
};
|
|
7814
7825
|
this.upsertMessage(updatedMessage);
|
|
7826
|
+
(_a = this.abortController) == null ? void 0 : _a.abort();
|
|
7827
|
+
this.abortController = new AbortController();
|
|
7828
|
+
this.setStreaming(true);
|
|
7815
7829
|
const approvalConfig = this.config.approval;
|
|
7816
7830
|
const onDecision = approvalConfig && typeof approvalConfig === "object" ? approvalConfig.onDecision : void 0;
|
|
7817
7831
|
try {
|
|
@@ -7842,7 +7856,7 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
7842
7856
|
if (!response.ok) {
|
|
7843
7857
|
const errorData = await response.json().catch(() => null);
|
|
7844
7858
|
throw new Error(
|
|
7845
|
-
(
|
|
7859
|
+
(_b = errorData == null ? void 0 : errorData.error) != null ? _b : `Approval request failed: ${response.status}`
|
|
7846
7860
|
);
|
|
7847
7861
|
}
|
|
7848
7862
|
stream = response.body;
|
|
@@ -7850,23 +7864,35 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
7850
7864
|
stream = response;
|
|
7851
7865
|
}
|
|
7852
7866
|
if (stream) {
|
|
7853
|
-
await this.connectStream(stream);
|
|
7854
|
-
} else
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7867
|
+
await this.connectStream(stream, { allowReentry: true });
|
|
7868
|
+
} else {
|
|
7869
|
+
if (decision === "denied") {
|
|
7870
|
+
this.appendMessage({
|
|
7871
|
+
id: `denial-${approval.id}`,
|
|
7872
|
+
role: "assistant",
|
|
7873
|
+
content: "Tool execution was denied by user.",
|
|
7874
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7875
|
+
streaming: false,
|
|
7876
|
+
sequence: this.nextSequence()
|
|
7877
|
+
});
|
|
7878
|
+
}
|
|
7879
|
+
this.setStreaming(false);
|
|
7880
|
+
this.abortController = null;
|
|
7863
7881
|
}
|
|
7882
|
+
} else {
|
|
7883
|
+
this.setStreaming(false);
|
|
7884
|
+
this.abortController = null;
|
|
7864
7885
|
}
|
|
7865
7886
|
} catch (error) {
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
)
|
|
7887
|
+
const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("abort"));
|
|
7888
|
+
this.setStreaming(false);
|
|
7889
|
+
this.abortController = null;
|
|
7890
|
+
if (!isAbortError) {
|
|
7891
|
+
(_d = (_c = this.callbacks).onError) == null ? void 0 : _d.call(
|
|
7892
|
+
_c,
|
|
7893
|
+
error instanceof Error ? error : new Error(String(error))
|
|
7894
|
+
);
|
|
7895
|
+
}
|
|
7870
7896
|
}
|
|
7871
7897
|
}
|
|
7872
7898
|
/**
|
|
@@ -7920,7 +7946,7 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
7920
7946
|
});
|
|
7921
7947
|
}
|
|
7922
7948
|
async resolveAskUserQuestion(toolMessage, answer) {
|
|
7923
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
7949
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
7924
7950
|
const live = this.messages.find((m) => m.id === toolMessage.id);
|
|
7925
7951
|
if (((_a = live == null ? void 0 : live.agentMetadata) == null ? void 0 : _a.askUserQuestionAnswered) === true) return;
|
|
7926
7952
|
const executionId = (_b = toolMessage.agentMetadata) == null ? void 0 : _b.executionId;
|
|
@@ -7944,8 +7970,11 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
7944
7970
|
}
|
|
7945
7971
|
}
|
|
7946
7972
|
this.markAskUserQuestionResolved(toolMessage, structuredAnswers);
|
|
7973
|
+
(_h = this.abortController) == null ? void 0 : _h.abort();
|
|
7974
|
+
this.abortController = new AbortController();
|
|
7975
|
+
this.setStreaming(true);
|
|
7947
7976
|
const toolCallId = toolMessage.toolCall.id;
|
|
7948
|
-
const args = (
|
|
7977
|
+
const args = (_i = toolMessage.toolCall) == null ? void 0 : _i.args;
|
|
7949
7978
|
const questions = Array.isArray(args == null ? void 0 : args.questions) ? args.questions : [];
|
|
7950
7979
|
if (questions.length === 0) {
|
|
7951
7980
|
const fallback = typeof answer === "string" ? answer : Object.entries(answer).map(
|
|
@@ -7991,17 +8020,25 @@ var AgentWidgetSession = class _AgentWidgetSession {
|
|
|
7991
8020
|
if (!response.ok) {
|
|
7992
8021
|
const errorData = await response.json().catch(() => null);
|
|
7993
8022
|
throw new Error(
|
|
7994
|
-
(
|
|
8023
|
+
(_j = errorData == null ? void 0 : errorData.error) != null ? _j : `Resume failed: ${response.status}`
|
|
7995
8024
|
);
|
|
7996
8025
|
}
|
|
7997
8026
|
if (response.body) {
|
|
7998
|
-
await this.connectStream(response.body);
|
|
8027
|
+
await this.connectStream(response.body, { allowReentry: true });
|
|
8028
|
+
} else {
|
|
8029
|
+
this.setStreaming(false);
|
|
8030
|
+
this.abortController = null;
|
|
7999
8031
|
}
|
|
8000
8032
|
} catch (error) {
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
)
|
|
8033
|
+
const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("abort"));
|
|
8034
|
+
this.setStreaming(false);
|
|
8035
|
+
this.abortController = null;
|
|
8036
|
+
if (!isAbortError) {
|
|
8037
|
+
(_l = (_k = this.callbacks).onError) == null ? void 0 : _l.call(
|
|
8038
|
+
_k,
|
|
8039
|
+
error instanceof Error ? error : new Error(String(error))
|
|
8040
|
+
);
|
|
8041
|
+
}
|
|
8005
8042
|
}
|
|
8006
8043
|
}
|
|
8007
8044
|
cancel() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtypelabs/persona",
|
|
3
|
-
"version": "3.21.
|
|
3
|
+
"version": "3.21.2",
|
|
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
|
@@ -2717,6 +2717,240 @@ describe('AgentWidgetClient - stopReason propagation', () => {
|
|
|
2717
2717
|
});
|
|
2718
2718
|
});
|
|
2719
2719
|
|
|
2720
|
+
// ============================================================================
|
|
2721
|
+
// Within-turn text/tool interleaving — assistant text bubbles must seal at
|
|
2722
|
+
// each agent_tool_start so the chronological text→tool→text→tool sequence
|
|
2723
|
+
// renders as distinct timeline entries instead of one merged bubble that
|
|
2724
|
+
// appears below all the tool cards.
|
|
2725
|
+
// ============================================================================
|
|
2726
|
+
|
|
2727
|
+
describe('AgentWidgetClient - agent_turn text/tool interleaving', () => {
|
|
2728
|
+
const collectMessages = (events: AgentWidgetEvent[]): AgentWidgetMessage[] => {
|
|
2729
|
+
const byId = new Map<string, AgentWidgetMessage>();
|
|
2730
|
+
const order: string[] = [];
|
|
2731
|
+
for (const e of events) {
|
|
2732
|
+
if (e.type !== 'message') continue;
|
|
2733
|
+
if (!byId.has(e.message.id)) order.push(e.message.id);
|
|
2734
|
+
byId.set(e.message.id, e.message);
|
|
2735
|
+
}
|
|
2736
|
+
return order.map((id) => byId.get(id)!);
|
|
2737
|
+
};
|
|
2738
|
+
|
|
2739
|
+
it('seals the assistant text bubble at each agent_tool_start so subsequent text creates a new bubble', async () => {
|
|
2740
|
+
const execId = 'exec_interleave';
|
|
2741
|
+
global.fetch = createAgentStreamFetch([
|
|
2742
|
+
sseEvent('agent_start', {
|
|
2743
|
+
executionId: execId, agentId: 'virtual', agentName: 'Test',
|
|
2744
|
+
maxTurns: 1, startedAt: new Date().toISOString(), seq: 1,
|
|
2745
|
+
}),
|
|
2746
|
+
sseEvent('agent_iteration_start', {
|
|
2747
|
+
executionId: execId, iteration: 1, maxTurns: 1,
|
|
2748
|
+
startedAt: new Date().toISOString(), seq: 2,
|
|
2749
|
+
}),
|
|
2750
|
+
sseEvent('agent_turn_start', {
|
|
2751
|
+
executionId: execId, iteration: 1, turnIndex: 0,
|
|
2752
|
+
role: 'assistant', turnId: 'turn_1', seq: 3,
|
|
2753
|
+
}),
|
|
2754
|
+
sseEvent('agent_turn_delta', {
|
|
2755
|
+
executionId: execId, iteration: 1, delta: 'before tool 1',
|
|
2756
|
+
contentType: 'text', turnId: 'turn_1', seq: 4,
|
|
2757
|
+
}),
|
|
2758
|
+
sseEvent('agent_tool_start', {
|
|
2759
|
+
executionId: execId, iteration: 1,
|
|
2760
|
+
toolCallId: 'call_1', toolName: 'search', toolType: 'builtin', seq: 5,
|
|
2761
|
+
}),
|
|
2762
|
+
sseEvent('agent_tool_complete', {
|
|
2763
|
+
executionId: execId, iteration: 1,
|
|
2764
|
+
toolCallId: 'call_1', toolName: 'search', success: true,
|
|
2765
|
+
executionTime: 10, seq: 6,
|
|
2766
|
+
}),
|
|
2767
|
+
sseEvent('agent_turn_delta', {
|
|
2768
|
+
executionId: execId, iteration: 1, delta: 'between tools',
|
|
2769
|
+
contentType: 'text', turnId: 'turn_1', seq: 7,
|
|
2770
|
+
}),
|
|
2771
|
+
sseEvent('agent_tool_start', {
|
|
2772
|
+
executionId: execId, iteration: 1,
|
|
2773
|
+
toolCallId: 'call_2', toolName: 'fetch', toolType: 'builtin', seq: 8,
|
|
2774
|
+
}),
|
|
2775
|
+
sseEvent('agent_tool_complete', {
|
|
2776
|
+
executionId: execId, iteration: 1,
|
|
2777
|
+
toolCallId: 'call_2', toolName: 'fetch', success: true,
|
|
2778
|
+
executionTime: 10, seq: 9,
|
|
2779
|
+
}),
|
|
2780
|
+
sseEvent('agent_turn_delta', {
|
|
2781
|
+
executionId: execId, iteration: 1, delta: 'after tool 2',
|
|
2782
|
+
contentType: 'text', turnId: 'turn_1', seq: 10,
|
|
2783
|
+
}),
|
|
2784
|
+
sseEvent('agent_turn_complete', {
|
|
2785
|
+
executionId: execId, iteration: 1, role: 'assistant',
|
|
2786
|
+
turnId: 'turn_1', completedAt: new Date().toISOString(),
|
|
2787
|
+
stopReason: 'end_turn', seq: 11,
|
|
2788
|
+
}),
|
|
2789
|
+
sseEvent('agent_iteration_complete', {
|
|
2790
|
+
executionId: execId, iteration: 1, toolCallsMade: 2,
|
|
2791
|
+
stopConditionMet: true, completedAt: new Date().toISOString(), seq: 12,
|
|
2792
|
+
}),
|
|
2793
|
+
sseEvent('agent_complete', {
|
|
2794
|
+
executionId: execId, agentId: 'virtual', success: true,
|
|
2795
|
+
iterations: 1, stopReason: 'complete',
|
|
2796
|
+
completedAt: new Date().toISOString(), seq: 13,
|
|
2797
|
+
}),
|
|
2798
|
+
]);
|
|
2799
|
+
|
|
2800
|
+
const client = new AgentWidgetClient({
|
|
2801
|
+
apiUrl: 'http://localhost:8000',
|
|
2802
|
+
agent: { name: 'Test', model: 'openai:gpt-4o-mini', systemPrompt: 'test' },
|
|
2803
|
+
});
|
|
2804
|
+
const events: AgentWidgetEvent[] = [];
|
|
2805
|
+
await client.dispatch(
|
|
2806
|
+
{ messages: [{ id: 'usr_1', role: 'user', content: 'Hi', createdAt: new Date().toISOString() }] },
|
|
2807
|
+
(e) => events.push(e)
|
|
2808
|
+
);
|
|
2809
|
+
|
|
2810
|
+
const messages = collectMessages(events);
|
|
2811
|
+
const assistants = messages.filter((m) => m.role === 'assistant' && m.variant !== 'tool');
|
|
2812
|
+
const tools = messages.filter((m) => m.variant === 'tool');
|
|
2813
|
+
|
|
2814
|
+
expect(assistants.map((m) => m.content)).toEqual([
|
|
2815
|
+
'before tool 1',
|
|
2816
|
+
'between tools',
|
|
2817
|
+
'after tool 2',
|
|
2818
|
+
]);
|
|
2819
|
+
expect(tools.map((m) => m.toolCall?.name)).toEqual(['search', 'fetch']);
|
|
2820
|
+
expect(new Set(assistants.map((m) => m.id)).size).toBe(3);
|
|
2821
|
+
});
|
|
2822
|
+
|
|
2823
|
+
it('attaches agent_turn_complete.stopReason to the final assistant text segment when the turn ends with text', async () => {
|
|
2824
|
+
const execId = 'exec_stopreason_tail_text';
|
|
2825
|
+
global.fetch = createAgentStreamFetch([
|
|
2826
|
+
sseEvent('agent_start', {
|
|
2827
|
+
executionId: execId, agentId: 'virtual', agentName: 'Test',
|
|
2828
|
+
maxTurns: 1, startedAt: new Date().toISOString(), seq: 1,
|
|
2829
|
+
}),
|
|
2830
|
+
sseEvent('agent_iteration_start', {
|
|
2831
|
+
executionId: execId, iteration: 1, maxTurns: 1,
|
|
2832
|
+
startedAt: new Date().toISOString(), seq: 2,
|
|
2833
|
+
}),
|
|
2834
|
+
sseEvent('agent_turn_start', {
|
|
2835
|
+
executionId: execId, iteration: 1, turnIndex: 0,
|
|
2836
|
+
role: 'assistant', turnId: 'turn_1', seq: 3,
|
|
2837
|
+
}),
|
|
2838
|
+
sseEvent('agent_turn_delta', {
|
|
2839
|
+
executionId: execId, iteration: 1, delta: 'first segment',
|
|
2840
|
+
contentType: 'text', turnId: 'turn_1', seq: 4,
|
|
2841
|
+
}),
|
|
2842
|
+
sseEvent('agent_tool_start', {
|
|
2843
|
+
executionId: execId, iteration: 1,
|
|
2844
|
+
toolCallId: 'call_1', toolName: 'search', toolType: 'builtin', seq: 5,
|
|
2845
|
+
}),
|
|
2846
|
+
sseEvent('agent_tool_complete', {
|
|
2847
|
+
executionId: execId, iteration: 1,
|
|
2848
|
+
toolCallId: 'call_1', toolName: 'search', success: true,
|
|
2849
|
+
executionTime: 10, seq: 6,
|
|
2850
|
+
}),
|
|
2851
|
+
sseEvent('agent_turn_delta', {
|
|
2852
|
+
executionId: execId, iteration: 1, delta: 'final segment',
|
|
2853
|
+
contentType: 'text', turnId: 'turn_1', seq: 7,
|
|
2854
|
+
}),
|
|
2855
|
+
sseEvent('agent_turn_complete', {
|
|
2856
|
+
executionId: execId, iteration: 1, role: 'assistant',
|
|
2857
|
+
turnId: 'turn_1', completedAt: new Date().toISOString(),
|
|
2858
|
+
stopReason: 'length', seq: 8,
|
|
2859
|
+
}),
|
|
2860
|
+
sseEvent('agent_iteration_complete', {
|
|
2861
|
+
executionId: execId, iteration: 1, toolCallsMade: 1,
|
|
2862
|
+
stopConditionMet: true, completedAt: new Date().toISOString(), seq: 9,
|
|
2863
|
+
}),
|
|
2864
|
+
sseEvent('agent_complete', {
|
|
2865
|
+
executionId: execId, agentId: 'virtual', success: true,
|
|
2866
|
+
iterations: 1, stopReason: 'complete',
|
|
2867
|
+
completedAt: new Date().toISOString(), seq: 10,
|
|
2868
|
+
}),
|
|
2869
|
+
]);
|
|
2870
|
+
|
|
2871
|
+
const client = new AgentWidgetClient({
|
|
2872
|
+
apiUrl: 'http://localhost:8000',
|
|
2873
|
+
agent: { name: 'Test', model: 'openai:gpt-4o-mini', systemPrompt: 'test' },
|
|
2874
|
+
});
|
|
2875
|
+
const events: AgentWidgetEvent[] = [];
|
|
2876
|
+
await client.dispatch(
|
|
2877
|
+
{ messages: [{ id: 'usr_1', role: 'user', content: 'Hi', createdAt: new Date().toISOString() }] },
|
|
2878
|
+
(e) => events.push(e)
|
|
2879
|
+
);
|
|
2880
|
+
|
|
2881
|
+
const assistants = collectMessages(events).filter(
|
|
2882
|
+
(m) => m.role === 'assistant' && m.variant !== 'tool'
|
|
2883
|
+
);
|
|
2884
|
+
expect(assistants).toHaveLength(2);
|
|
2885
|
+
expect(assistants[0].content).toBe('first segment');
|
|
2886
|
+
expect(assistants[0].stopReason).toBeUndefined();
|
|
2887
|
+
expect(assistants[1].content).toBe('final segment');
|
|
2888
|
+
expect(assistants[1].stopReason).toBe('length');
|
|
2889
|
+
});
|
|
2890
|
+
|
|
2891
|
+
it('attaches agent_turn_complete.stopReason to the preceding text segment when the turn ends with a tool call', async () => {
|
|
2892
|
+
const execId = 'exec_stopreason_tail_tool';
|
|
2893
|
+
global.fetch = createAgentStreamFetch([
|
|
2894
|
+
sseEvent('agent_start', {
|
|
2895
|
+
executionId: execId, agentId: 'virtual', agentName: 'Test',
|
|
2896
|
+
maxTurns: 1, startedAt: new Date().toISOString(), seq: 1,
|
|
2897
|
+
}),
|
|
2898
|
+
sseEvent('agent_iteration_start', {
|
|
2899
|
+
executionId: execId, iteration: 1, maxTurns: 1,
|
|
2900
|
+
startedAt: new Date().toISOString(), seq: 2,
|
|
2901
|
+
}),
|
|
2902
|
+
sseEvent('agent_turn_start', {
|
|
2903
|
+
executionId: execId, iteration: 1, turnIndex: 0,
|
|
2904
|
+
role: 'assistant', turnId: 'turn_1', seq: 3,
|
|
2905
|
+
}),
|
|
2906
|
+
sseEvent('agent_turn_delta', {
|
|
2907
|
+
executionId: execId, iteration: 1, delta: 'about to call tool',
|
|
2908
|
+
contentType: 'text', turnId: 'turn_1', seq: 4,
|
|
2909
|
+
}),
|
|
2910
|
+
sseEvent('agent_tool_start', {
|
|
2911
|
+
executionId: execId, iteration: 1,
|
|
2912
|
+
toolCallId: 'call_1', toolName: 'search', toolType: 'builtin', seq: 5,
|
|
2913
|
+
}),
|
|
2914
|
+
sseEvent('agent_tool_complete', {
|
|
2915
|
+
executionId: execId, iteration: 1,
|
|
2916
|
+
toolCallId: 'call_1', toolName: 'search', success: true,
|
|
2917
|
+
executionTime: 10, seq: 6,
|
|
2918
|
+
}),
|
|
2919
|
+
sseEvent('agent_turn_complete', {
|
|
2920
|
+
executionId: execId, iteration: 1, role: 'assistant',
|
|
2921
|
+
turnId: 'turn_1', completedAt: new Date().toISOString(),
|
|
2922
|
+
stopReason: 'max_tool_calls', seq: 7,
|
|
2923
|
+
}),
|
|
2924
|
+
sseEvent('agent_iteration_complete', {
|
|
2925
|
+
executionId: execId, iteration: 1, toolCallsMade: 1,
|
|
2926
|
+
stopConditionMet: true, completedAt: new Date().toISOString(), seq: 8,
|
|
2927
|
+
}),
|
|
2928
|
+
sseEvent('agent_complete', {
|
|
2929
|
+
executionId: execId, agentId: 'virtual', success: true,
|
|
2930
|
+
iterations: 1, stopReason: 'max_tool_calls',
|
|
2931
|
+
completedAt: new Date().toISOString(), seq: 9,
|
|
2932
|
+
}),
|
|
2933
|
+
]);
|
|
2934
|
+
|
|
2935
|
+
const client = new AgentWidgetClient({
|
|
2936
|
+
apiUrl: 'http://localhost:8000',
|
|
2937
|
+
agent: { name: 'Test', model: 'openai:gpt-4o-mini', systemPrompt: 'test' },
|
|
2938
|
+
});
|
|
2939
|
+
const events: AgentWidgetEvent[] = [];
|
|
2940
|
+
await client.dispatch(
|
|
2941
|
+
{ messages: [{ id: 'usr_1', role: 'user', content: 'Hi', createdAt: new Date().toISOString() }] },
|
|
2942
|
+
(e) => events.push(e)
|
|
2943
|
+
);
|
|
2944
|
+
|
|
2945
|
+
const assistants = collectMessages(events).filter(
|
|
2946
|
+
(m) => m.role === 'assistant' && m.variant !== 'tool'
|
|
2947
|
+
);
|
|
2948
|
+
expect(assistants).toHaveLength(1);
|
|
2949
|
+
expect(assistants[0].content).toBe('about to call tool');
|
|
2950
|
+
expect(assistants[0].stopReason).toBe('max_tool_calls');
|
|
2951
|
+
});
|
|
2952
|
+
});
|
|
2953
|
+
|
|
2720
2954
|
// ============================================================================
|
|
2721
2955
|
// step_await (LOCAL tool pause) + resumeFlow
|
|
2722
2956
|
// ============================================================================
|