@teaflask/assistant 0.8.0 → 0.10.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/dist/components/assistant-transcript.js +1 -7
- package/dist/components/attachment-chips.js +11 -5
- package/dist/components/composer.js +5 -6
- package/dist/components/message-list.js +2 -1
- package/dist/components/open-minted-url.js +12 -0
- package/dist/components/primitives/button.js +3 -0
- package/dist/components/teaflask-assistant-provider.d.ts +4 -3
- package/dist/components/tool-call-rows.js +1 -8
- package/dist/components/tool-row.js +1 -2
- package/dist/components/tool-view-slot.js +0 -1
- package/dist/components/tool-views/view-dom.d.ts +3 -4
- package/dist/components/tool-views/view-dom.js +1 -4
- package/dist/components/transcript-follow.js +12 -0
- package/dist/components/use-child-replay-stream.js +9 -2
- package/dist/components/use-transcript.d.ts +3 -2
- package/dist/components/user-message.js +26 -1
- package/dist/contract/events.d.ts +1 -0
- package/dist/contract/events.js +1 -0
- package/dist/contract/telemetry.d.ts +23 -2
- package/dist/contract/threads.d.ts +0 -5
- package/dist/contract/threads.js +0 -1
- package/dist/core/assistant-session-registry.js +10 -0
- package/dist/core/composer-policy.d.ts +1 -2
- package/dist/core/composer-policy.js +0 -2
- package/dist/core/connection-epoch.d.ts +4 -5
- package/dist/core/connection-epoch.js +41 -12
- package/dist/core/conversation-adoption.js +7 -0
- package/dist/core/conversation-contract.d.ts +1 -0
- package/dist/core/conversation-store.js +0 -1
- package/dist/core/epoch-sync.js +3 -1
- package/dist/core/error-copy.js +9 -1
- package/dist/core/long-message.js +22 -0
- package/dist/core/message-copy.js +1 -0
- package/dist/core/tool-call-display.d.ts +1 -7
- package/dist/core/tool-call-presentation.js +0 -3
- package/dist/core/tool-outcome.js +0 -1
- package/dist/core/tool-view.d.ts +2 -7
- package/dist/core/transcript-rows.d.ts +0 -6
- package/dist/core/transcript-rows.js +0 -3
- package/dist/core/turn-failed-anchors.d.ts +7 -0
- package/dist/core/turn-failed-anchors.js +42 -0
- package/dist/core/withdrawn-anchors.d.ts +11 -0
- package/dist/core/withdrawn-anchors.js +76 -0
- package/dist/generated/models/apiErrorCode.d.ts +3 -0
- package/dist/generated/models/apiErrorCode.js +3 -0
- package/dist/generated/models/assistantStreamMarkerName.d.ts +1 -0
- package/dist/generated/models/assistantStreamMarkerName.js +1 -0
- package/dist/generated/models/failureReasonCode.d.ts +33 -0
- package/dist/generated/models/failureReasonCode.js +10 -0
- package/dist/generated/models/index.d.ts +2 -0
- package/dist/generated/models/index.js +2 -0
- package/dist/generated/models/replyWithdrawnMarker.d.ts +4 -0
- package/dist/generated/models/replyWithdrawnMarker.js +1 -0
- package/dist/generated/models/servingErrorCode.d.ts +5 -4
- package/dist/generated/models/servingErrorCode.js +1 -0
- package/dist/generated/models/turnFailedMarker.d.ts +3 -0
- package/dist/headless.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +0 -1
- package/dist/styles.css +1 -1
- package/dist/transport/replay-stream-agent.js +4 -0
- package/dist/transport/stream-resume.d.ts +6 -2
- package/dist/transport/stream-resume.js +12 -4
- package/package.json +1 -1
- package/dist/core/tool-offload-anchors.js +0 -7
|
@@ -14,11 +14,11 @@ import { toolCancelRecorder, wellFormedToolCancelAnchorsOf, } from "./tool-cance
|
|
|
14
14
|
import { toolErrorRecorder, wellFormedToolErrorAnchorsOf, } from "./tool-error-anchors.js";
|
|
15
15
|
import { toolDecisionRecorder, wellFormedToolDecisionAnchorsOf, } from "./tool-decision-anchors.js";
|
|
16
16
|
import { toolDenialRecorder, wellFormedToolDenialAnchorsOf, } from "./tool-denial-anchors.js";
|
|
17
|
-
import { toolOffloadRecorder, wellFormedToolOffloadAnchorsOf, } from "./tool-offload-anchors.js";
|
|
18
17
|
import { toolRefusalRecorder, wellFormedToolRefusalAnchorsOf, } from "./tool-refusal-anchors.js";
|
|
19
|
-
import { turnFailedMarkerRecorder, wellFormedFailAnchorsOf, } from "./turn-failed-anchors.js";
|
|
18
|
+
import { failedTurnRecorder, turnFailedMarkerRecorder, wellFormedFailAnchorsOf, } from "./turn-failed-anchors.js";
|
|
20
19
|
import { turnUsageRecorder, wellFormedTurnUsageAnchorsOf, } from "./turn-usage-anchors.js";
|
|
21
20
|
import { messagesRecorder, streamErrorRecorder } from "./messages-cell.js";
|
|
21
|
+
import { withdrawnReplyRecorder, withoutWithdrawnReplies, } from "./withdrawn-anchors.js";
|
|
22
22
|
import { userTurnInjector } from "./user-turn-ledger.js";
|
|
23
23
|
export function markerAnchorsOf(resume) {
|
|
24
24
|
return {
|
|
@@ -28,7 +28,6 @@ export function markerAnchorsOf(resume) {
|
|
|
28
28
|
toolErrorAnchors: wellFormedToolErrorAnchorsOf(resume.toolErrorAnchors),
|
|
29
29
|
toolCancelAnchors: wellFormedToolCancelAnchorsOf(resume.toolCancelAnchors),
|
|
30
30
|
toolRefusalAnchors: wellFormedToolRefusalAnchorsOf(resume.toolRefusalAnchors),
|
|
31
|
-
toolOffloadAnchors: wellFormedToolOffloadAnchorsOf(resume.toolOffloadAnchors),
|
|
32
31
|
toolDecisionAnchors: wellFormedToolDecisionAnchorsOf(resume.toolDecisionAnchors),
|
|
33
32
|
toolDenialAnchors: wellFormedToolDenialAnchorsOf(resume.toolDenialLedger.denied),
|
|
34
33
|
toolCallDisplayAnchors: wellFormedToolCallDisplayAnchorsOf(resume.toolCallDisplayAnchors),
|
|
@@ -83,6 +82,7 @@ export function beginConnectionEpoch(deps) {
|
|
|
83
82
|
publishEntries(executionInbox.entries());
|
|
84
83
|
}
|
|
85
84
|
},
|
|
85
|
+
visibleMessages: () => _visibleMessagesOf(agent, deps),
|
|
86
86
|
dispose: () => {
|
|
87
87
|
for (const subscription of subscriptions) {
|
|
88
88
|
subscription.unsubscribe();
|
|
@@ -92,6 +92,9 @@ export function beginConnectionEpoch(deps) {
|
|
|
92
92
|
},
|
|
93
93
|
};
|
|
94
94
|
}
|
|
95
|
+
function _visibleMessagesOf(agent, deps) {
|
|
96
|
+
return withoutWithdrawnReplies([...agent.messages], deps.resume.withdrawnReplies);
|
|
97
|
+
}
|
|
95
98
|
function _runMarkerSubscriptionsOf(agent, deps) {
|
|
96
99
|
return [
|
|
97
100
|
agent.subscribe(resumeMarkerRecorder((runId, messageId, markerValue, markerTimestamp) => {
|
|
@@ -103,17 +106,48 @@ function _runMarkerSubscriptionsOf(agent, deps) {
|
|
|
103
106
|
deps.noteInterruptAnswerConsumed(runId, consumed.interruptId, consumed.round);
|
|
104
107
|
})),
|
|
105
108
|
agent.subscribe(turnFailedMarkerRecorder((failedRunId, messageId, position, receipts) => {
|
|
106
|
-
if (deps.resume.recordTurnFailedReceipts(failedRunId, messageId, position, receipts)) {
|
|
109
|
+
if (deps.resume.recordTurnFailedReceipts(failedRunId, _visibleAnchorOf(messageId, agent, deps), position, receipts)) {
|
|
107
110
|
deps.publishMarkerAnchors();
|
|
108
111
|
}
|
|
109
112
|
})),
|
|
113
|
+
agent.subscribe(failedTurnRecorder((failed) => {
|
|
114
|
+
if (!_isNewsToTheHost(failed, deps)) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
deps.onTelemetry({
|
|
118
|
+
name: "turn_failed",
|
|
119
|
+
properties: {
|
|
120
|
+
thread_id: deps.threadId,
|
|
121
|
+
turn_id: failed.turnId,
|
|
122
|
+
reason_code: failed.reasonCode,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
})),
|
|
110
126
|
agent.subscribe(subagentDeliveryMarkerRecorder((deliveryRunId, messageId, results) => {
|
|
111
127
|
if (deps.resume.recordSubagentDelivery(deliveryRunId, messageId, results)) {
|
|
112
128
|
deps.publishMarkerAnchors();
|
|
113
129
|
}
|
|
114
130
|
})),
|
|
131
|
+
agent.subscribe(withdrawnReplyRecorder((marker) => {
|
|
132
|
+
const before = deps.resume.withdrawnReplies;
|
|
133
|
+
deps.resume.recordRepliesWithdrawn(marker);
|
|
134
|
+
if (deps.resume.withdrawnReplies !== before) {
|
|
135
|
+
deps.publishMessages(_visibleMessagesOf(agent, deps));
|
|
136
|
+
}
|
|
137
|
+
})),
|
|
115
138
|
];
|
|
116
139
|
}
|
|
140
|
+
function _isNewsToTheHost(failed, deps) {
|
|
141
|
+
return (failed.turnId !== deps.settledFailedTurnId &&
|
|
142
|
+
deps.resume.recordFailedTurnReported(failed.turnId));
|
|
143
|
+
}
|
|
144
|
+
function _visibleAnchorOf(messageId, agent, deps) {
|
|
145
|
+
const visible = _visibleMessagesOf(agent, deps);
|
|
146
|
+
if (visible.some((message) => message.id === messageId)) {
|
|
147
|
+
return messageId;
|
|
148
|
+
}
|
|
149
|
+
return visible.at(-1)?.id ?? messageId;
|
|
150
|
+
}
|
|
117
151
|
function _toolMarkerSubscriptionsOf(agent, deps) {
|
|
118
152
|
return [
|
|
119
153
|
agent.subscribe(toolErrorRecorder((toolCallId, errorText) => {
|
|
@@ -151,13 +185,6 @@ function _toolMarkerSubscriptionsOf(agent, deps) {
|
|
|
151
185
|
deps.publishMarkerAnchors();
|
|
152
186
|
}
|
|
153
187
|
})),
|
|
154
|
-
agent.subscribe(toolOffloadRecorder((toolCallId) => {
|
|
155
|
-
const before = deps.resume.toolOffloadAnchors;
|
|
156
|
-
deps.resume.recordToolOffload(toolCallId);
|
|
157
|
-
if (deps.resume.toolOffloadAnchors !== before) {
|
|
158
|
-
deps.publishMarkerAnchors();
|
|
159
|
-
}
|
|
160
|
-
})),
|
|
161
188
|
agent.subscribe(toolCallDisplayRecorder((toolCallId, display) => {
|
|
162
189
|
const before = deps.resume.toolCallDisplayAnchors;
|
|
163
190
|
deps.resume.recordToolCallDisplay(toolCallId, display);
|
|
@@ -226,7 +253,9 @@ function _streamStateSubscriptionsOf(agent, deps, inboxes) {
|
|
|
226
253
|
deps.onQuietClose(() => agent.isRunning);
|
|
227
254
|
})),
|
|
228
255
|
agent.subscribe(executionInboxRecorder(inboxes.executionInbox, inboxes.publishEntries)),
|
|
229
|
-
agent.subscribe(messagesRecorder(
|
|
256
|
+
agent.subscribe(messagesRecorder((messages) => {
|
|
257
|
+
deps.publishMessages(withoutWithdrawnReplies(messages, deps.resume.withdrawnReplies));
|
|
258
|
+
})),
|
|
230
259
|
agent.subscribe(streamErrorRecorder(deps.onStreamError)),
|
|
231
260
|
];
|
|
232
261
|
}
|
|
@@ -117,6 +117,7 @@ function _finishAdoption(store, detail) {
|
|
|
117
117
|
resume,
|
|
118
118
|
approvalInbox,
|
|
119
119
|
coworkerRequestTurns: detail.turns,
|
|
120
|
+
settledFailedTurnId: _settledFailedTurnIdOf(detail.turns),
|
|
120
121
|
};
|
|
121
122
|
if (!continuing) {
|
|
122
123
|
store.questionDrafts.clearAll();
|
|
@@ -152,6 +153,10 @@ function adoptCoworkerRequests(store, turns) {
|
|
|
152
153
|
publishApprovalCards(store, active.approvalInbox.cards());
|
|
153
154
|
}
|
|
154
155
|
}
|
|
156
|
+
function _settledFailedTurnIdOf(turns) {
|
|
157
|
+
const newest = turns.at(-1);
|
|
158
|
+
return newest?.status === "failed" ? newest.id : null;
|
|
159
|
+
}
|
|
155
160
|
export function adoptRefreshedDetail(store, detail) {
|
|
156
161
|
const active = store._active;
|
|
157
162
|
if (active?.thread.id !== detail.thread.id) {
|
|
@@ -168,6 +173,7 @@ export function adoptRefreshedDetail(store, detail) {
|
|
|
168
173
|
resume: active.resume,
|
|
169
174
|
approvalInbox: active.approvalInbox,
|
|
170
175
|
coworkerRequestTurns: active.coworkerRequestTurns,
|
|
176
|
+
settledFailedTurnId: active.settledFailedTurnId,
|
|
171
177
|
};
|
|
172
178
|
}
|
|
173
179
|
_adoptWireModelPick(store, detail.thread, { evenWhenDirty: false });
|
|
@@ -217,6 +223,7 @@ export function enterConversationFromSend(store, thread, turn, sentPick) {
|
|
|
217
223
|
resume,
|
|
218
224
|
approvalInbox,
|
|
219
225
|
coworkerRequestTurns: [],
|
|
226
|
+
settledFailedTurnId: null,
|
|
220
227
|
};
|
|
221
228
|
if (sameModelPick(store._modelPick, sentPick)) {
|
|
222
229
|
_adoptWireModelPick(store, thread, { evenWhenDirty: true });
|
|
@@ -17,6 +17,7 @@ export interface ActiveConversation {
|
|
|
17
17
|
resume: StreamResumeStore;
|
|
18
18
|
approvalInbox: ApprovalInbox;
|
|
19
19
|
coworkerRequestTurns: readonly ServingAssistantTurn[];
|
|
20
|
+
settledFailedTurnId: string | null;
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
22
23
|
* State and controls for writing and sending messages in the open conversation,
|
|
@@ -132,7 +132,6 @@ export class AssistantConversationStore {
|
|
|
132
132
|
toolErrorAnchors: new Map(),
|
|
133
133
|
toolRefusalAnchors: new Map(),
|
|
134
134
|
toolCancelAnchors: new Set(),
|
|
135
|
-
toolOffloadAnchors: new Set(),
|
|
136
135
|
toolDecisionAnchors: new Set(),
|
|
137
136
|
toolDenialAnchors: new Set(),
|
|
138
137
|
toolCallDisplayAnchors: new Map(),
|
package/dist/core/epoch-sync.js
CHANGED
|
@@ -38,7 +38,7 @@ export function syncEpoch(store) {
|
|
|
38
38
|
store._questionAnswers.clear();
|
|
39
39
|
publishExecutionEntries(store, []);
|
|
40
40
|
store._epoch.withExecutionInbox((inbox) => inbox.adoptCoworkerRequests(active.coworkerRequestTurns));
|
|
41
|
-
store._messagesCell.set(
|
|
41
|
+
store._messagesCell.set(store._epoch.visibleMessages());
|
|
42
42
|
store._markerAnchorsCell.set(markerAnchorsOf(active.resume));
|
|
43
43
|
store._connectionCell.set({
|
|
44
44
|
epochId,
|
|
@@ -89,6 +89,8 @@ function _mintEpochDeps(store, active, epochId) {
|
|
|
89
89
|
onRunSettled: store.handleRunSettled,
|
|
90
90
|
onQuietClose: store.handleStreamClosedQuietly,
|
|
91
91
|
onWorkflowRestarted: store.handleWorkflowRestarted,
|
|
92
|
+
onTelemetry: store.deps.onTelemetry,
|
|
93
|
+
settledFailedTurnId: active.settledFailedTurnId,
|
|
92
94
|
};
|
|
93
95
|
}
|
|
94
96
|
export function retireEpoch(store) {
|
package/dist/core/error-copy.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { ServingApiError } from "../transport/serving-error.js";
|
|
2
|
+
import { MESSAGE_TOO_LONG_SENTENCE } from "./message-copy.js";
|
|
2
3
|
import { STOP_NOT_DELIVERED_SENTENCE } from "./stop-copy.js";
|
|
3
4
|
const GENERIC_FAILURE_SENTENCE = "Something went wrong sending that. Please try again.";
|
|
4
5
|
export function userSentenceFor(error) {
|
|
5
6
|
if (!(error instanceof ServingApiError)) {
|
|
6
7
|
return GENERIC_FAILURE_SENTENCE;
|
|
7
8
|
}
|
|
9
|
+
if (_isAMessageTooLong(error)) {
|
|
10
|
+
return MESSAGE_TOO_LONG_SENTENCE;
|
|
11
|
+
}
|
|
8
12
|
switch (error.code) {
|
|
9
13
|
case "ASSISTANT_THREAD_BUSY":
|
|
10
14
|
return ("The assistant is already answering — possibly in another tab. " +
|
|
@@ -20,7 +24,7 @@ export function userSentenceFor(error) {
|
|
|
20
24
|
case "NOT_FOUND":
|
|
21
25
|
return "That conversation isn't available.";
|
|
22
26
|
case "VALIDATION_ERROR":
|
|
23
|
-
return "That message couldn't be sent.
|
|
27
|
+
return "That message couldn't be sent. Check it and try again.";
|
|
24
28
|
default:
|
|
25
29
|
return error.message;
|
|
26
30
|
}
|
|
@@ -37,6 +41,10 @@ export function failureSentenceOf(error) {
|
|
|
37
41
|
}
|
|
38
42
|
return "Something went wrong. Try again.";
|
|
39
43
|
}
|
|
44
|
+
function _isAMessageTooLong(error) {
|
|
45
|
+
return (error.code === "MESSAGE_TOO_LONG" ||
|
|
46
|
+
(error.status === 413 && error.code === "UNKNOWN"));
|
|
47
|
+
}
|
|
40
48
|
function _rateLimitedSentence(retryAfterSeconds) {
|
|
41
49
|
if (retryAfterSeconds !== null && retryAfterSeconds > 0) {
|
|
42
50
|
return `You're sending messages too quickly. Try again in ${String(retryAfterSeconds)} seconds.`;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const LONG_MESSAGE_COLLAPSE_FROM = 20_000;
|
|
2
|
+
const PREVIEW_CHARS = 600;
|
|
3
|
+
const WORD_BREAK_WINDOW = 120;
|
|
4
|
+
export function longMessagePreviewOf(text) {
|
|
5
|
+
if (text.length <= LONG_MESSAGE_COLLAPSE_FROM) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
return text.slice(0, _previewEndOf(text)).trimEnd();
|
|
9
|
+
}
|
|
10
|
+
function _previewEndOf(text) {
|
|
11
|
+
const windowStart = PREVIEW_CHARS - WORD_BREAK_WINDOW;
|
|
12
|
+
const lastBreak = text.slice(windowStart, PREVIEW_CHARS).search(/\s\S*$/);
|
|
13
|
+
if (lastBreak >= 0) {
|
|
14
|
+
return windowStart + lastBreak;
|
|
15
|
+
}
|
|
16
|
+
return _isLowSurrogate(text.charCodeAt(PREVIEW_CHARS))
|
|
17
|
+
? PREVIEW_CHARS - 1
|
|
18
|
+
: PREVIEW_CHARS;
|
|
19
|
+
}
|
|
20
|
+
function _isLowSurrogate(code) {
|
|
21
|
+
return code >= 0xdc00 && code <= 0xdfff;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const MESSAGE_TOO_LONG_SENTENCE = "This message is too long to answer. Please shorten it or split it into smaller messages.";
|
|
@@ -63,15 +63,9 @@ export interface ToolCallViewModel {
|
|
|
63
63
|
argsText?: string;
|
|
64
64
|
/**
|
|
65
65
|
* The raw result text. Undefined until the call finishes, and withheld for
|
|
66
|
-
* calls that failed, were not approved, didn't run
|
|
67
|
-
* their result replaced by a preview.
|
|
66
|
+
* calls that failed, were not approved, didn't run or were declined.
|
|
68
67
|
*/
|
|
69
68
|
output?: string;
|
|
70
|
-
/**
|
|
71
|
-
* True when the result was too large for the agent and was replaced by a
|
|
72
|
-
* shortened preview. `output` is then withheld.
|
|
73
|
-
*/
|
|
74
|
-
offloaded?: boolean;
|
|
75
69
|
/** Why the call failed, set when it failed. */
|
|
76
70
|
errorText?: string;
|
|
77
71
|
/**
|
package/dist/core/tool-view.d.ts
CHANGED
|
@@ -40,8 +40,8 @@ export interface ToolViewCall<TArgs = Record<string, unknown>, TResult = unknown
|
|
|
40
40
|
result?: TResult;
|
|
41
41
|
/**
|
|
42
42
|
* The raw result text as recorded. Undefined until the call finishes, and
|
|
43
|
-
* withheld for calls that failed, were not approved, didn't run
|
|
44
|
-
* declined
|
|
43
|
+
* withheld for calls that failed, were not approved, didn't run or were
|
|
44
|
+
* declined.
|
|
45
45
|
*/
|
|
46
46
|
resultText?: string;
|
|
47
47
|
/**
|
|
@@ -50,11 +50,6 @@ export interface ToolViewCall<TArgs = Record<string, unknown>, TResult = unknown
|
|
|
50
50
|
* complete.
|
|
51
51
|
*/
|
|
52
52
|
truncated?: boolean;
|
|
53
|
-
/**
|
|
54
|
-
* True when the result was too large for the agent and was replaced by a
|
|
55
|
-
* shortened preview. The tool's real output is not available to the view.
|
|
56
|
-
*/
|
|
57
|
-
offloaded?: boolean;
|
|
58
53
|
/**
|
|
59
54
|
* Why the call failed, set when it failed. Use humanFailureSentenceOf to
|
|
60
55
|
* get a sentence fit for the user, and actionFailureOf to read an action's
|
|
@@ -110,12 +110,6 @@ export interface ToolCallRow {
|
|
|
110
110
|
* arrives.
|
|
111
111
|
*/
|
|
112
112
|
result?: string;
|
|
113
|
-
/**
|
|
114
|
-
* True when the tool's output was too large and `result` holds a shortened
|
|
115
|
-
* stand-in rather than the actual output; show a "result shortened" note
|
|
116
|
-
* instead of the content.
|
|
117
|
-
*/
|
|
118
|
-
offloaded: boolean;
|
|
119
113
|
/** Why the call failed. Present only when state is output-error. */
|
|
120
114
|
errorText?: string;
|
|
121
115
|
/**
|
|
@@ -21,7 +21,6 @@ export function transcriptRowsOf(messages, anchors, running, metaByMessageId = N
|
|
|
21
21
|
errors: anchors.toolErrorAnchors,
|
|
22
22
|
refusals: anchors.toolRefusalAnchors,
|
|
23
23
|
cancels: anchors.toolCancelAnchors,
|
|
24
|
-
offloads: anchors.toolOffloadAnchors,
|
|
25
24
|
decisions: anchors.toolDecisionAnchors ?? EMPTY_TOOL_DECISION_ANCHORS,
|
|
26
25
|
denials: anchors.toolDenialAnchors ?? EMPTY_TOOL_DENIAL_ANCHORS,
|
|
27
26
|
displays: anchors.toolCallDisplayAnchors,
|
|
@@ -112,7 +111,6 @@ function _pushMessageRows(rows, message, results, context) {
|
|
|
112
111
|
state,
|
|
113
112
|
argsText: toolCall.function.arguments,
|
|
114
113
|
result,
|
|
115
|
-
offloaded: context.offloads.has(toolCall.id),
|
|
116
114
|
errorText: errorTextOf(recordedError, result),
|
|
117
115
|
...(state === "refused" ? { refusalText: recordedRefusal } : {}),
|
|
118
116
|
display: context.displays.get(toolCall.id),
|
|
@@ -239,7 +237,6 @@ export function transcriptBlocksOf(messages, anchors, running, metaByMessageId =
|
|
|
239
237
|
errors: anchors.toolErrorAnchors,
|
|
240
238
|
refusals: anchors.toolRefusalAnchors,
|
|
241
239
|
cancels: anchors.toolCancelAnchors,
|
|
242
|
-
offloads: anchors.toolOffloadAnchors,
|
|
243
240
|
decisions: anchors.toolDecisionAnchors ?? EMPTY_TOOL_DECISION_ANCHORS,
|
|
244
241
|
denials: anchors.toolDenialAnchors ?? EMPTY_TOOL_DENIAL_ANCHORS,
|
|
245
242
|
displays: anchors.toolCallDisplayAnchors,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { receiptOf, wellFormedReceiptAnchorsOf, withReceiptAnchored, type AnchoredReceipts } from "./receipt-anchors.js";
|
|
2
|
+
import { FailureReasonCode } from "../generated/models/index.js";
|
|
2
3
|
import type { AgentSubscriber } from "@ag-ui/client";
|
|
3
4
|
/**
|
|
4
5
|
* The failure sentences for a failed turn, split by whether they are shown
|
|
@@ -10,3 +11,9 @@ export declare const turnFailedReceiptOf: typeof receiptOf;
|
|
|
10
11
|
export declare const withTurnFailedAnchored: typeof withReceiptAnchored;
|
|
11
12
|
export declare const wellFormedFailAnchorsOf: typeof wellFormedReceiptAnchorsOf;
|
|
12
13
|
export declare function turnFailedMarkerRecorder(onReceiptAnchored: (failedRunId: string, messageId: string, position: keyof AnchoredTurnFailReceipts, receipts: string[]) => void): AgentSubscriber;
|
|
14
|
+
export interface FailedTurn {
|
|
15
|
+
turnId: string;
|
|
16
|
+
reasonCode: FailureReasonCode;
|
|
17
|
+
}
|
|
18
|
+
export declare function failedTurnOf(value: unknown): FailedTurn | null;
|
|
19
|
+
export declare function failedTurnRecorder(onFailedLive: (failed: FailedTurn) => void): AgentSubscriber;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { receiptMarkerRecorder, receiptOf, wellFormedReceiptAnchorsOf, withReceiptAnchored, } from "./receipt-anchors.js";
|
|
2
2
|
import { TURN_FAILED_EVENT_NAME } from "../contract/events.js";
|
|
3
|
+
import { FailureReasonCode, } from "../generated/models/index.js";
|
|
3
4
|
export const EMPTY_TURN_FAILED_ANCHORS = new Map();
|
|
4
5
|
export const turnFailedReceiptOf = receiptOf;
|
|
5
6
|
export const withTurnFailedAnchored = withReceiptAnchored;
|
|
@@ -7,3 +8,44 @@ export const wellFormedFailAnchorsOf = wellFormedReceiptAnchorsOf;
|
|
|
7
8
|
export function turnFailedMarkerRecorder(onReceiptAnchored) {
|
|
8
9
|
return receiptMarkerRecorder(TURN_FAILED_EVENT_NAME, onReceiptAnchored);
|
|
9
10
|
}
|
|
11
|
+
export function failedTurnOf(value) {
|
|
12
|
+
if (typeof value !== "object" || value === null) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const payload = value;
|
|
16
|
+
if (typeof payload.turnId !== "string" || payload.turnId === "") {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
turnId: payload.turnId,
|
|
21
|
+
reasonCode: _knownCodeOf(payload.reasonCode),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function _knownCodeOf(code) {
|
|
25
|
+
const known = Object.values(FailureReasonCode);
|
|
26
|
+
return known.includes(code)
|
|
27
|
+
? code
|
|
28
|
+
: FailureReasonCode.internal_error;
|
|
29
|
+
}
|
|
30
|
+
export function failedTurnRecorder(onFailedLive) {
|
|
31
|
+
let pending = null;
|
|
32
|
+
return {
|
|
33
|
+
onRunInitialized() {
|
|
34
|
+
pending = null;
|
|
35
|
+
},
|
|
36
|
+
onCustomEvent({ event }) {
|
|
37
|
+
if (event.name === TURN_FAILED_EVENT_NAME) {
|
|
38
|
+
pending = failedTurnOf(event.value);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
onRunFinishedEvent() {
|
|
42
|
+
pending = null;
|
|
43
|
+
},
|
|
44
|
+
onRunErrorEvent() {
|
|
45
|
+
if (pending !== null) {
|
|
46
|
+
onFailedLive(pending);
|
|
47
|
+
}
|
|
48
|
+
pending = null;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AgentSubscriber } from "@ag-ui/client";
|
|
2
|
+
import type { Message } from "@ag-ui/core";
|
|
3
|
+
import type { ReplyWithdrawnMarker } from "../generated/models/index.js";
|
|
4
|
+
export interface WithdrawnReplies {
|
|
5
|
+
messageIds: ReadonlySet<string>;
|
|
6
|
+
toolCallIds: ReadonlySet<string>;
|
|
7
|
+
}
|
|
8
|
+
export declare const EMPTY_WITHDRAWN_REPLIES: WithdrawnReplies;
|
|
9
|
+
export declare function withRepliesWithdrawn(previous: WithdrawnReplies, marker: ReplyWithdrawnMarker): WithdrawnReplies;
|
|
10
|
+
export declare function withdrawnReplyRecorder(onWithdrawn: (marker: ReplyWithdrawnMarker) => void): AgentSubscriber;
|
|
11
|
+
export declare function withoutWithdrawnReplies(messages: readonly Message[], withdrawn: WithdrawnReplies): readonly Message[];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { REPLY_WITHDRAWN_EVENT_NAME } from "../contract/events.js";
|
|
2
|
+
export const EMPTY_WITHDRAWN_REPLIES = {
|
|
3
|
+
messageIds: new Set(),
|
|
4
|
+
toolCallIds: new Set(),
|
|
5
|
+
};
|
|
6
|
+
function _withdrawnRepliesOf(name, value) {
|
|
7
|
+
if (name !== REPLY_WITHDRAWN_EVENT_NAME ||
|
|
8
|
+
typeof value !== "object" ||
|
|
9
|
+
value === null) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const record = value;
|
|
13
|
+
return {
|
|
14
|
+
messageIds: _idsIn(record.messageIds),
|
|
15
|
+
toolCallIds: _idsIn(record.toolCallIds),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function _idsIn(field) {
|
|
19
|
+
if (!Array.isArray(field)) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
return field.filter((id) => typeof id === "string" && id !== "");
|
|
23
|
+
}
|
|
24
|
+
export function withRepliesWithdrawn(previous, marker) {
|
|
25
|
+
const messageIds = _withIds(previous.messageIds, marker.messageIds);
|
|
26
|
+
const toolCallIds = _withIds(previous.toolCallIds, marker.toolCallIds);
|
|
27
|
+
if (messageIds === previous.messageIds &&
|
|
28
|
+
toolCallIds === previous.toolCallIds) {
|
|
29
|
+
return previous;
|
|
30
|
+
}
|
|
31
|
+
return { messageIds, toolCallIds };
|
|
32
|
+
}
|
|
33
|
+
function _withIds(previous, ids) {
|
|
34
|
+
if (ids.every((id) => previous.has(id))) {
|
|
35
|
+
return previous;
|
|
36
|
+
}
|
|
37
|
+
return new Set([...previous, ...ids]);
|
|
38
|
+
}
|
|
39
|
+
export function withdrawnReplyRecorder(onWithdrawn) {
|
|
40
|
+
return {
|
|
41
|
+
onCustomEvent({ event }) {
|
|
42
|
+
const marker = _withdrawnRepliesOf(event.name, event.value);
|
|
43
|
+
if (marker !== null) {
|
|
44
|
+
onWithdrawn(marker);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function withoutWithdrawnReplies(messages, withdrawn) {
|
|
50
|
+
if (_nothingIsWithdrawn(withdrawn)) {
|
|
51
|
+
return messages;
|
|
52
|
+
}
|
|
53
|
+
return messages.flatMap((message) => _visiblePartOf(message, withdrawn));
|
|
54
|
+
}
|
|
55
|
+
function _nothingIsWithdrawn(withdrawn) {
|
|
56
|
+
return withdrawn.messageIds.size === 0 && withdrawn.toolCallIds.size === 0;
|
|
57
|
+
}
|
|
58
|
+
function _visiblePartOf(message, withdrawn) {
|
|
59
|
+
if (withdrawn.messageIds.has(message.id)) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
if (message.role === "tool") {
|
|
63
|
+
return withdrawn.toolCallIds.has(message.toolCallId) ? [] : [message];
|
|
64
|
+
}
|
|
65
|
+
if (message.role !== "assistant" || message.toolCalls === undefined) {
|
|
66
|
+
return [message];
|
|
67
|
+
}
|
|
68
|
+
const toolCalls = message.toolCalls.filter((toolCall) => !withdrawn.toolCallIds.has(toolCall.id));
|
|
69
|
+
if (toolCalls.length === message.toolCalls.length) {
|
|
70
|
+
return [message];
|
|
71
|
+
}
|
|
72
|
+
if (toolCalls.length === 0 && !message.content) {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
return [{ ...message, toolCalls }];
|
|
76
|
+
}
|
|
@@ -22,6 +22,7 @@ export declare const ApiErrorCode: {
|
|
|
22
22
|
readonly DOC_REVISION_NOT_PUBLISHABLE: "DOC_REVISION_NOT_PUBLISHABLE";
|
|
23
23
|
readonly DOC_REVISION_NOT_DISCARDABLE: "DOC_REVISION_NOT_DISCARDABLE";
|
|
24
24
|
readonly DOC_PROPOSAL_STALE: "DOC_PROPOSAL_STALE";
|
|
25
|
+
readonly REVISION_STALE: "REVISION_STALE";
|
|
25
26
|
readonly DOC_LIMIT_REACHED: "DOC_LIMIT_REACHED";
|
|
26
27
|
readonly HELP_SITE_CONFIG_INVALID: "HELP_SITE_CONFIG_INVALID";
|
|
27
28
|
readonly HELP_SITE_NEVER_PUBLISHED: "HELP_SITE_NEVER_PUBLISHED";
|
|
@@ -115,6 +116,7 @@ export declare const ApiErrorCode: {
|
|
|
115
116
|
readonly WORK_CREDIT_REQUEST_PENDING: "WORK_CREDIT_REQUEST_PENDING";
|
|
116
117
|
readonly JOIN_REQUEST_ALREADY_MEMBER: "JOIN_REQUEST_ALREADY_MEMBER";
|
|
117
118
|
readonly JOIN_REQUEST_ALREADY_PENDING: "JOIN_REQUEST_ALREADY_PENDING";
|
|
119
|
+
readonly LAST_OWNER: "LAST_OWNER";
|
|
118
120
|
readonly ORGANIZATION_DOMAIN_DNS_CLAIMED: "ORGANIZATION_DOMAIN_DNS_CLAIMED";
|
|
119
121
|
readonly EMAIL_DOMAIN_NOT_CLAIMABLE: "EMAIL_DOMAIN_NOT_CLAIMABLE";
|
|
120
122
|
readonly EMAIL_DOMAIN_REVOKED: "EMAIL_DOMAIN_REVOKED";
|
|
@@ -150,6 +152,7 @@ export declare const ApiErrorCode: {
|
|
|
150
152
|
readonly MCP_CAPABILITIES_INVALID: "MCP_CAPABILITIES_INVALID";
|
|
151
153
|
readonly MCP_AUTHORIZATION_SERVER_UNAVAILABLE: "MCP_AUTHORIZATION_SERVER_UNAVAILABLE";
|
|
152
154
|
readonly MCP_CONNECTION_CONFLICT: "MCP_CONNECTION_CONFLICT";
|
|
155
|
+
readonly MESSAGE_TOO_LONG: "MESSAGE_TOO_LONG";
|
|
153
156
|
readonly DEMO_ERROR: "DEMO_ERROR";
|
|
154
157
|
readonly HTTP_ERROR: "HTTP_ERROR";
|
|
155
158
|
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
|
|
@@ -21,6 +21,7 @@ export const ApiErrorCode = {
|
|
|
21
21
|
DOC_REVISION_NOT_PUBLISHABLE: "DOC_REVISION_NOT_PUBLISHABLE",
|
|
22
22
|
DOC_REVISION_NOT_DISCARDABLE: "DOC_REVISION_NOT_DISCARDABLE",
|
|
23
23
|
DOC_PROPOSAL_STALE: "DOC_PROPOSAL_STALE",
|
|
24
|
+
REVISION_STALE: "REVISION_STALE",
|
|
24
25
|
DOC_LIMIT_REACHED: "DOC_LIMIT_REACHED",
|
|
25
26
|
HELP_SITE_CONFIG_INVALID: "HELP_SITE_CONFIG_INVALID",
|
|
26
27
|
HELP_SITE_NEVER_PUBLISHED: "HELP_SITE_NEVER_PUBLISHED",
|
|
@@ -114,6 +115,7 @@ export const ApiErrorCode = {
|
|
|
114
115
|
WORK_CREDIT_REQUEST_PENDING: "WORK_CREDIT_REQUEST_PENDING",
|
|
115
116
|
JOIN_REQUEST_ALREADY_MEMBER: "JOIN_REQUEST_ALREADY_MEMBER",
|
|
116
117
|
JOIN_REQUEST_ALREADY_PENDING: "JOIN_REQUEST_ALREADY_PENDING",
|
|
118
|
+
LAST_OWNER: "LAST_OWNER",
|
|
117
119
|
ORGANIZATION_DOMAIN_DNS_CLAIMED: "ORGANIZATION_DOMAIN_DNS_CLAIMED",
|
|
118
120
|
EMAIL_DOMAIN_NOT_CLAIMABLE: "EMAIL_DOMAIN_NOT_CLAIMABLE",
|
|
119
121
|
EMAIL_DOMAIN_REVOKED: "EMAIL_DOMAIN_REVOKED",
|
|
@@ -149,6 +151,7 @@ export const ApiErrorCode = {
|
|
|
149
151
|
MCP_CAPABILITIES_INVALID: "MCP_CAPABILITIES_INVALID",
|
|
150
152
|
MCP_AUTHORIZATION_SERVER_UNAVAILABLE: "MCP_AUTHORIZATION_SERVER_UNAVAILABLE",
|
|
151
153
|
MCP_CONNECTION_CONFLICT: "MCP_CONNECTION_CONFLICT",
|
|
154
|
+
MESSAGE_TOO_LONG: "MESSAGE_TOO_LONG",
|
|
152
155
|
DEMO_ERROR: "DEMO_ERROR",
|
|
153
156
|
HTTP_ERROR: "HTTP_ERROR",
|
|
154
157
|
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a reply failed, coarse on purpose since it is visible in the browser.
|
|
3
|
+
* "model_unavailable": the AI model did not respond; "rate_limited": too many
|
|
4
|
+
* requests right now; "credential_declined": an API key or connected ChatGPT
|
|
5
|
+
* plan was refused; "out_of_credit": your Teaflask organization has no funds to
|
|
6
|
+
* pay for replies; "too_long": the message or conversation is too long;
|
|
7
|
+
* "content_refused": the model's content policy refused the message;
|
|
8
|
+
* "configuration_error": the assistant's setup asks for something its model
|
|
9
|
+
* cannot do; "internal_error": anything else. Treat an unknown code as
|
|
10
|
+
* "internal_error".
|
|
11
|
+
*/
|
|
12
|
+
export type FailureReasonCode = (typeof FailureReasonCode)[keyof typeof FailureReasonCode];
|
|
13
|
+
/**
|
|
14
|
+
* Why a reply failed, coarse on purpose since it is visible in the browser.
|
|
15
|
+
* "model_unavailable": the AI model did not respond; "rate_limited": too many
|
|
16
|
+
* requests right now; "credential_declined": an API key or connected ChatGPT
|
|
17
|
+
* plan was refused; "out_of_credit": your Teaflask organization has no funds to
|
|
18
|
+
* pay for replies; "too_long": the message or conversation is too long;
|
|
19
|
+
* "content_refused": the model's content policy refused the message;
|
|
20
|
+
* "configuration_error": the assistant's setup asks for something its model
|
|
21
|
+
* cannot do; "internal_error": anything else. Treat an unknown code as
|
|
22
|
+
* "internal_error".
|
|
23
|
+
*/
|
|
24
|
+
export declare const FailureReasonCode: {
|
|
25
|
+
readonly model_unavailable: "model_unavailable";
|
|
26
|
+
readonly rate_limited: "rate_limited";
|
|
27
|
+
readonly credential_declined: "credential_declined";
|
|
28
|
+
readonly out_of_credit: "out_of_credit";
|
|
29
|
+
readonly too_long: "too_long";
|
|
30
|
+
readonly content_refused: "content_refused";
|
|
31
|
+
readonly configuration_error: "configuration_error";
|
|
32
|
+
readonly internal_error: "internal_error";
|
|
33
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const FailureReasonCode = {
|
|
2
|
+
model_unavailable: "model_unavailable",
|
|
3
|
+
rate_limited: "rate_limited",
|
|
4
|
+
credential_declined: "credential_declined",
|
|
5
|
+
out_of_credit: "out_of_credit",
|
|
6
|
+
too_long: "too_long",
|
|
7
|
+
content_refused: "content_refused",
|
|
8
|
+
configuration_error: "configuration_error",
|
|
9
|
+
internal_error: "internal_error",
|
|
10
|
+
};
|
|
@@ -45,6 +45,7 @@ export * from "./errorEnvelope.js";
|
|
|
45
45
|
export * from "./evidenceAfterVoidRecordedMarker.js";
|
|
46
46
|
export * from "./evidenceAfterVoidRecordedMarkerError.js";
|
|
47
47
|
export * from "./evidenceAfterVoidRecordedMarkerResult.js";
|
|
48
|
+
export * from "./failureReasonCode.js";
|
|
48
49
|
export * from "./fileSource.js";
|
|
49
50
|
export * from "./functionCall.js";
|
|
50
51
|
export * from "./getServingAssistantThreadParams.js";
|
|
@@ -83,6 +84,7 @@ export * from "./readPageResult.js";
|
|
|
83
84
|
export * from "./reasoningMessage.js";
|
|
84
85
|
export * from "./reasoningMessageMetadata.js";
|
|
85
86
|
export * from "./refusedField.js";
|
|
87
|
+
export * from "./replyWithdrawnMarker.js";
|
|
86
88
|
export * from "./resolvedLanding.js";
|
|
87
89
|
export * from "./resolvedLandingCard.js";
|
|
88
90
|
export * from "./resolveServingTurnApprovalRequest.js";
|
|
@@ -45,6 +45,7 @@ export * from "./errorEnvelope.js";
|
|
|
45
45
|
export * from "./evidenceAfterVoidRecordedMarker.js";
|
|
46
46
|
export * from "./evidenceAfterVoidRecordedMarkerError.js";
|
|
47
47
|
export * from "./evidenceAfterVoidRecordedMarkerResult.js";
|
|
48
|
+
export * from "./failureReasonCode.js";
|
|
48
49
|
export * from "./fileSource.js";
|
|
49
50
|
export * from "./functionCall.js";
|
|
50
51
|
export * from "./getServingAssistantThreadParams.js";
|
|
@@ -83,6 +84,7 @@ export * from "./readPageResult.js";
|
|
|
83
84
|
export * from "./reasoningMessage.js";
|
|
84
85
|
export * from "./reasoningMessageMetadata.js";
|
|
85
86
|
export * from "./refusedField.js";
|
|
87
|
+
export * from "./replyWithdrawnMarker.js";
|
|
86
88
|
export * from "./resolvedLanding.js";
|
|
87
89
|
export * from "./resolvedLandingCard.js";
|
|
88
90
|
export * from "./resolveServingTurnApprovalRequest.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|