@assistant-ui/eve 0.0.24 → 0.0.26

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 (37) hide show
  1. package/dist/convertEveMessages.d.ts +40 -51
  2. package/dist/convertEveMessages.d.ts.map +1 -1
  3. package/dist/convertEveMessages.js +146 -117
  4. package/dist/convertEveMessages.js.map +1 -1
  5. package/dist/deriveCreatedAt.d.ts +9 -9
  6. package/dist/deriveCreatedAt.d.ts.map +1 -1
  7. package/dist/deriveCreatedAt.js +25 -34
  8. package/dist/deriveCreatedAt.js.map +1 -1
  9. package/dist/eveCloudSessions.js +1 -2
  10. package/dist/eveExtras.d.ts +4 -2
  11. package/dist/eveExtras.d.ts.map +1 -1
  12. package/dist/eveExtras.js +1 -2
  13. package/dist/eveExtras.js.map +1 -1
  14. package/dist/hooks.d.ts +4 -3
  15. package/dist/hooks.d.ts.map +1 -1
  16. package/dist/hooks.js +5 -5
  17. package/dist/hooks.js.map +1 -1
  18. package/dist/sdkIdentity.js +2 -3
  19. package/dist/useEveAgentRuntime.d.ts +3 -5
  20. package/dist/useEveAgentRuntime.d.ts.map +1 -1
  21. package/dist/useEveAgentRuntime.js +34 -23
  22. package/dist/useEveAgentRuntime.js.map +1 -1
  23. package/package.json +10 -10
  24. package/src/convertEveMessages.test.ts +925 -729
  25. package/src/convertEveMessages.ts +282 -227
  26. package/src/deriveCreatedAt.test.ts +90 -88
  27. package/src/deriveCreatedAt.ts +32 -36
  28. package/src/eveExtras.ts +4 -4
  29. package/src/hooks.ts +4 -3
  30. package/src/testUtils.ts +189 -8
  31. package/src/useEveAgentRuntime.cloud.test.tsx +105 -136
  32. package/src/useEveAgentRuntime.eveResume.test.tsx +22 -104
  33. package/src/useEveAgentRuntime.fast-refresh.test.tsx +195 -37
  34. package/src/useEveAgentRuntime.interruptedTurn.test.tsx +86 -40
  35. package/src/useEveAgentRuntime.refetch.test.tsx +19 -58
  36. package/src/useEveAgentRuntime.test.tsx +372 -291
  37. package/src/useEveAgentRuntime.ts +100 -113
@@ -1,11 +1,11 @@
1
1
  import { type AppendMessage, type RespondToToolApprovalOptions, type ThreadMessage } from "@assistant-ui/core";
2
- import type { EveAuthorizationOutcome, EveAuthorizationPart, EveMessage, EveMessageData, EveMessageInputRequest } from "eve/react";
3
- import type { InputResponse } from "eve/client";
4
- import type { MessageStreamEvent } from "eve/client";
2
+ import type { EveMessage, Interaction } from "eve/react";
3
+ import type { ConversationState, SessionStreamEvent } from "eve/client";
4
+ import { type Answer } from "eve/events";
5
5
  export type ConvertEveMessagesOptions = {
6
6
  /**
7
- * Marks the last assistant message as running while Eve is submitting or
8
- * streaming. When omitted, a message carrying Eve's `"streaming"` marker is
7
+ * Marks the last assistant message as running while Eve has a turn in
8
+ * flight. When omitted, a message carrying Eve's `"streaming"` marker is
9
9
  * treated as running; pass `false` to settle interrupted messages to a
10
10
  * terminal status.
11
11
  */
@@ -16,25 +16,22 @@ export type ConvertEveMessagesOptions = {
16
16
  readonly error?: unknown;
17
17
  readonly getCreatedAt?: ((message: EveMessage) => Date) | undefined;
18
18
  /**
19
- * The session's stream events. A `turn.failed` or `turn.cancelled` event
20
- * marks its turn's assistant message as incomplete, with a failure's code
21
- * and message as its error.
19
+ * The session's stream events. A failed or cancelled turn marks its
20
+ * assistant message as incomplete, with a failure's code and message as its
21
+ * error.
22
22
  */
23
- readonly events?: readonly MessageStreamEvent[] | undefined;
23
+ readonly events?: readonly SessionStreamEvent[] | undefined;
24
24
  };
25
25
  /**
26
- * Payload of the `authorization` data part the Eve runtime emits for a
27
- * connector authorization challenge. `state` discriminates a pending challenge
28
- * from a settled one; every other field is present only when Eve projected it.
29
- * This converter drops a `url` whose scheme is not `http(s)`, so a renderer can
30
- * link to it without checking the scheme itself; Eve itself passes through
31
- * whatever the connector supplied. Eve's
32
- * `turnId` and `stepIndex` part identity is omitted: a renderer receives the
33
- * one part it renders, in Eve's own step order, so display never has to
34
- * re-identify a challenge among several.
26
+ * Payload of the `authorization` data part the Eve runtime emits for a sign-in
27
+ * interaction. `state` discriminates a pending sign-in from a settled one;
28
+ * every other field is present only when Eve projected it. This converter
29
+ * drops a `url` whose scheme is not `http(s)`, so a renderer can link to it
30
+ * without checking the scheme itself; Eve itself passes through whatever the
31
+ * connector supplied.
35
32
  */
36
33
  export type EveAuthorizationData = {
37
- readonly state: EveAuthorizationPart["state"];
34
+ readonly state: "required" | "completed";
38
35
  readonly name: string;
39
36
  readonly displayName?: string;
40
37
  readonly description?: string;
@@ -42,7 +39,7 @@ export type EveAuthorizationData = {
42
39
  readonly userCode?: string;
43
40
  readonly instructions?: string;
44
41
  readonly expiresAt?: string;
45
- readonly outcome?: EveAuthorizationOutcome;
42
+ readonly outcome?: "authorized" | "declined" | "failed" | "timed-out" | "cancelled";
46
43
  readonly reason?: string;
47
44
  };
48
45
  /**
@@ -50,23 +47,18 @@ export type EveAuthorizationData = {
50
47
  */
51
48
  export declare const convertEveMessage: (message: EveMessage, index: number, messages: readonly EveMessage[], options?: ConvertEveMessagesOptions) => ThreadMessage;
52
49
  export type InterruptedTurnEventCache = {
53
- lastEvents: readonly MessageStreamEvent[];
54
- interruptions: readonly MessageStreamEvent[];
50
+ lastEvents: readonly SessionStreamEvent[];
51
+ interruptions: readonly SessionStreamEvent[];
55
52
  };
53
+ export declare const collectInterruptedTurnEvents: (events: readonly SessionStreamEvent[], cache: InterruptedTurnEventCache) => readonly SessionStreamEvent[];
56
54
  /**
57
- * Collects the `turn.failed` and `turn.cancelled` events of an append-only
58
- * event log, scanning only the events appended since the cached call. The
59
- * returned array keeps its identity until a new interruption arrives.
55
+ * Converts Eve's conversation messages into assistant-ui thread messages.
60
56
  */
61
- export declare const collectInterruptedTurnEvents: (events: readonly MessageStreamEvent[], cache: InterruptedTurnEventCache) => readonly MessageStreamEvent[];
57
+ export declare const convertEveMessages: (data: Pick<ConversationState, "messages">, options?: ConvertEveMessagesOptions) => ThreadMessage[];
62
58
  /**
63
- * Converts the full Eve message data object into assistant-ui thread messages.
64
- */
65
- export declare const convertEveMessages: (data: EveMessageData, options?: ConvertEveMessagesOptions) => ThreadMessage[];
66
- /**
67
- * Structural subset of the `string | UserContent` message content Eve's `send`
68
- * API accepts. The helpers declare the shapes they produce and leave
69
- * assignability to the send call site, checked against the installed version.
59
+ * Structural subset of the message content Eve's `send` API accepts. The
60
+ * helpers declare the shapes they produce and leave assignability to the send
61
+ * call site, checked against the installed version.
70
62
  */
71
63
  export type EveMessageContent = string | ({
72
64
  readonly type: "text";
@@ -83,34 +75,31 @@ export type EveMessageContent = string | ({
83
75
  */
84
76
  export declare const getEveMessageContent: (message: AppendMessage) => EveMessageContent;
85
77
  /**
86
- * Finds the pending input request answered by the given approval id, so the
87
- * response mapping can honor the request's display mode.
78
+ * Finds the request of the interaction answered by the given approval id, so
79
+ * the answer mapping can honor the request's display mode.
88
80
  */
89
- export declare const findEveInputRequest: (data: EveMessageData, approvalId: string) => EveMessageInputRequest | undefined;
81
+ export declare const findEveInputRequest: (data: Pick<ConversationState, "messages">, approvalId: string) => Interaction["request"] | undefined;
90
82
  /**
91
- * Converts an assistant-ui tool approval response into an Eve input response.
83
+ * Converts an assistant-ui tool approval response into an Eve `Answer`.
92
84
  *
93
- * When `inputRequest` is known, every returned response carries either an
94
- * option the request declares or a free-form answer. A literal option match
95
- * wins, then the `"approve"` / `"cancel"` option the response's boolean decision
85
+ * When `inputRequest` is known, every returned answer carries an option the
86
+ * request declares, the literal `"approve"` / `"deny"` for an approval that
87
+ * declares no options, or a free-form answer. A literal option match wins,
88
+ * then the `"approve"` / `"deny"` option the response's boolean decision
96
89
  * names, then the response's `text` (or its `reason`, for a caller that
97
90
  * predates the first-class answer field) when the request takes a free-form
98
91
  * answer (`display: "text"`, `allowFreeform`, or no options at all, and never
99
92
  * `display: "confirmation"`) and the response is not a refusal.
100
- * A one-argument call, or a part with no request, still forwards a caller
101
- * `optionId` as it always has, including ids the request never declared.
102
93
  *
103
94
  * A response the known request cannot record as the answer it asked for throws
104
- * instead of being submitted, because eve resolves a request the moment any
105
- * response for it arrives, and an empty one is recorded as an answer with no
106
- * content. Not sending leaves the request pending, so the caller can retry or
95
+ * instead of being submitted, because Eve resolves an interaction the moment
96
+ * any answer for it arrives, and an empty one is recorded as an answer with no
97
+ * content. Not sending leaves the interaction open, so the caller can retry or
107
98
  * the user can answer it as an ordinary message.
108
99
  *
109
- * With no request to map against — a one-argument call, or a part carrying no
110
- * `toolMetadata.eve.inputRequest` — the response maps as it always has, to the
111
- * literal `"approve"` / `"cancel"` option every eve approval declares. Guessing
112
- * a display mode is what this mapper stopped doing; an unknown request is not
113
- * a guess about one.
100
+ * With no request to map against, a one-argument call forwards a caller
101
+ * `optionId` as is and otherwise maps the decision to the literal `"approve"` /
102
+ * `"deny"` option every Eve approval declares.
114
103
  */
115
- export declare const toEveInputResponse: (response: RespondToToolApprovalOptions, inputRequest?: EveMessageInputRequest) => InputResponse;
104
+ export declare const toEveInputResponse: (response: RespondToToolApprovalOptions, inputRequest?: Interaction["request"]) => Answer;
116
105
  //# sourceMappingURL=convertEveMessages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"convertEveMessages.d.ts","sourceRoot":"","sources":["../src/convertEveMessages.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,aAAa,EAOlB,KAAK,4BAA4B,EAEjC,KAAK,aAAa,EAMnB,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EAEpB,UAAU,EACV,cAAc,EACd,sBAAsB,EAEvB,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAqBrD,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACzC;;OAEG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACpE;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,kBAAkB,EAAE,GAAG,SAAS,CAAC;CAC7D,CAAC;AAmPF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,uBAAuB,CAAC;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAyIF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,UAAU,EACnB,OAAO,MAAM,EACb,UAAU,SAAS,UAAU,EAAE,EAC/B,UAAS,yBAA8B,KACtC,aAOA,CAAC;AA8CJ,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC1C,aAAa,EAAE,SAAS,kBAAkB,EAAE,CAAC;CAC9C,CAAC;AAKF;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,GACvC,QAAQ,SAAS,kBAAkB,EAAE,EACrC,OAAO,yBAAyB,KAC/B,SAAS,kBAAkB,EAiB7B,CAAC;AAoBF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,cAAc,EACpB,UAAS,yBAA8B,KACtC,aAAa,EAWf,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,CACI;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChD;IACE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B,CACJ,EAAE,CAAC;AAMR;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAC/B,SAAS,aAAa,KACrB,iBAgFF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC9B,MAAM,cAAc,EACpB,YAAY,MAAM,KACjB,sBAAsB,GAAG,SAS3B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,kBAAkB,GAC7B,UAAU,4BAA4B,EACtC,eAAe,sBAAsB,KACpC,aAyDF,CAAC"}
1
+ {"version":3,"file":"convertEveMessages.d.ts","sourceRoot":"","sources":["../src/convertEveMessages.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { fromThreadMessageLike, toAssistantError } from "@assistant-ui/core";
2
2
  import { httpUrlPattern, resolveFileMediaType, resolveFilePartSource, resolveImageMediaType, toMediaWireUrl } from "@assistant-ui/core/internal";
3
- //#region src/convertEveMessages.ts
3
+ import { APPROVE_OPTION_ID, DENY_OPTION_ID } from "eve/events";
4
+ // src/convertEveMessages.ts
4
5
  const ASSISTANT_COMPLETE_STATUS = {
5
6
  type: "complete",
6
7
  reason: "stop"
@@ -32,8 +33,9 @@ const toMessageStatus = (message, index, messages, options, interruptedTurns) =>
32
33
  const interrupted = message.metadata?.turnId === void 0 ? void 0 : interruptedTurns.get(message.metadata.turnId);
33
34
  if (interrupted) return interrupted;
34
35
  const isLast = index === messages.length - 1;
35
- const hasPendingApproval = message.parts.some((part) => part.type === "dynamic-tool" && part.state === "approval-requested");
36
- const hasPendingAuthorization = message.metadata?.status === "streaming" && (!isLast || options.error === void 0) && message.parts.some((part) => part.type === "authorization" && part.state === "required");
36
+ const interactions = message.parts.flatMap((part) => part.type === "interaction" ? [part] : part.type === "tool" && part.interaction ? [part.interaction] : []);
37
+ const hasPendingApproval = interactions.some((interaction) => interaction.status === "open" && interaction.request.kind !== "sign-in");
38
+ const hasPendingAuthorization = message.metadata?.status === "streaming" && (!isLast || options.error === void 0) && interactions.some((interaction) => interaction.status === "open" && interaction.request.kind === "sign-in");
37
39
  if (hasPendingApproval) return {
38
40
  type: "requires-action",
39
41
  reason: "tool-calls"
@@ -63,19 +65,19 @@ const toolApprovalOptionsFromInputRequest = (inputRequest) => {
63
65
  if (!options || options.length === 0) return void 0;
64
66
  return options.map((option) => ({
65
67
  id: option.id,
66
- kind: option.id === "approve" ? "allow-once" : option.id === "cancel" ? "reject-once" : `_${option.id}`,
68
+ kind: option.id === APPROVE_OPTION_ID ? "allow-once" : option.id === DENY_OPTION_ID ? "reject-once" : `_${option.id}`,
67
69
  ...option.label && { label: option.label },
68
70
  ...option.description && { description: option.description }
69
71
  }));
70
72
  };
71
73
  /**
72
- * Eve resolves a request the moment any response for it arrives, so the answer
73
- * shapes it accepts decide both what the renderer may offer and what
74
+ * Eve resolves an interaction the moment any answer for it arrives, so the
75
+ * answer shapes it accepts decide both what the renderer may offer and what
74
76
  * {@link toEveInputResponse} may submit. A request with no options and no
75
77
  * `display` takes a typed answer, which is why the absence of both is not the
76
78
  * same as a confirmation.
77
79
  */
78
- const isDroppedOptionApproval = (inputRequest) => inputRequest.kind === "tool-approval" && !inputRequest.options?.length;
80
+ const isDroppedOptionApproval = (inputRequest) => inputRequest.kind === "approval" && !inputRequest.options?.length;
79
81
  const acceptsFreeformAnswer = (inputRequest) => inputRequest.display !== "confirmation" && !isDroppedOptionApproval(inputRequest) && (inputRequest.display === "text" || inputRequest.allowFreeform === true || !inputRequest.options?.length);
80
82
  const toolApprovalDisplay = (inputRequest) => {
81
83
  if (isDroppedOptionApproval(inputRequest)) return "decision";
@@ -84,93 +86,119 @@ const toolApprovalDisplay = (inputRequest) => {
84
86
  case "select": return "select";
85
87
  case "text": return "text";
86
88
  default:
87
- if (inputRequest.kind === "tool-approval") return "decision";
89
+ if (inputRequest.kind === "approval") return "decision";
88
90
  return inputRequest.options?.length ? "select" : "text";
89
91
  }
90
92
  };
91
- const toApproval = (part) => {
92
- if (part.state !== "approval-requested" && part.state !== "approval-responded" && part.state !== "output-available" && part.state !== "output-error" && part.state !== "output-denied") return;
93
- const approval = part.approval;
94
- if (!approval) return void 0;
95
- const inputRequest = part.toolMetadata?.eve?.inputRequest;
96
- const options = toolApprovalOptionsFromInputRequest(inputRequest);
93
+ const toApproval = (interaction) => {
94
+ const request = interaction.request;
95
+ const options = toolApprovalOptionsFromInputRequest(request);
96
+ const settled = interaction.status === "settled";
97
+ const approved = interaction.outcome === "accepted" ? true : interaction.outcome === "declined" ? false : void 0;
97
98
  return {
98
- id: approval.id,
99
- ...inputRequest && {
100
- prompt: inputRequest.prompt,
101
- display: toolApprovalDisplay(inputRequest),
102
- ...acceptsFreeformAnswer(inputRequest) && { allowFreeform: true }
103
- },
104
- ...approval.approved !== void 0 && { approved: approval.approved },
105
- ...approval.reason && { reason: approval.reason },
106
- ...approval.isAutomatic !== void 0 && { isAutomatic: approval.isAutomatic },
99
+ id: interaction.interactionId,
100
+ prompt: request.prompt,
101
+ display: toolApprovalDisplay(request),
102
+ ...acceptsFreeformAnswer(request) && { allowFreeform: true },
103
+ ...settled && approved !== void 0 && { approved },
104
+ ...settled && approved === void 0 && { resolution: interaction.outcome === "expired" ? "expired" : "cancelled" },
105
+ ...interaction.reason && { reason: interaction.reason },
106
+ ...settled && interaction.answer?.optionId !== void 0 && { optionId: interaction.answer.optionId },
107
+ ...settled && interaction.answer?.text !== void 0 && { text: interaction.answer.text },
107
108
  ...options && { options }
108
109
  };
109
110
  };
110
- const toJsonSafeInputRequest = (inputRequest) => ({
111
- requestId: inputRequest.requestId,
112
- prompt: inputRequest.prompt,
113
- kind: inputRequest.kind,
114
- ...inputRequest.display !== void 0 && { display: inputRequest.display },
115
- ...inputRequest.allowFreeform !== void 0 && { allowFreeform: inputRequest.allowFreeform },
116
- ...inputRequest.options && { options: inputRequest.options.map((option) => ({
117
- id: option.id,
118
- label: option.label,
119
- ...option.description !== void 0 && { description: option.description },
120
- ...option.style !== void 0 && { style: option.style }
121
- })) }
122
- });
123
- const toToolProviderMetadata = (part) => {
124
- const inputRequest = part.toolMetadata?.eve?.inputRequest;
125
- if (!inputRequest) return void 0;
126
- return { eve: { inputRequest: toJsonSafeInputRequest(inputRequest) } };
111
+ const toToolProviderMetadata = (interaction) => {
112
+ const request = interaction.request;
113
+ return { eve: { interaction: {
114
+ interactionId: interaction.interactionId,
115
+ prompt: request.prompt,
116
+ kind: request.kind,
117
+ ...request.display !== void 0 && { display: request.display },
118
+ ...request.allowFreeform !== void 0 && { allowFreeform: request.allowFreeform },
119
+ ...request.options && { options: request.options.map((option) => ({ ...option })) }
120
+ } } };
127
121
  };
128
- const convertDynamicToolPart = (part) => {
129
- const approval = toApproval(part);
130
- const providerMetadata = toToolProviderMetadata(part);
122
+ const convertToolPart = (part) => {
131
123
  const toolCall = {
132
124
  type: "tool-call",
133
- toolCallId: part.toolCallId,
134
- toolName: part.toolName,
135
- args: toJsonObject(part.input),
136
- argsText: part.state === "input-streaming" ? "" : stringifyArgs(part.input),
137
- ...approval && { approval },
138
- ...providerMetadata && { providerMetadata }
125
+ toolCallId: part.callId,
126
+ toolName: part.name,
127
+ ...part.state !== "streaming" && { args: toJsonObject(part.input) },
128
+ argsText: part.state === "streaming" ? part.inputText : stringifyArgs(part.input),
129
+ ...part.interaction && {
130
+ approval: toApproval(part.interaction),
131
+ providerMetadata: toToolProviderMetadata(part.interaction)
132
+ }
139
133
  };
140
134
  switch (part.state) {
141
- case "output-available": return {
135
+ case "completed": return {
142
136
  ...toolCall,
143
137
  result: part.output
144
138
  };
145
- case "output-error": return {
139
+ case "failed":
140
+ case "rejected":
141
+ case "cancelled": return {
146
142
  ...toolCall,
147
- result: { error: part.errorText },
143
+ result: { error: part.error?.message ?? part.interaction?.reason ?? (part.state === "rejected" ? "Tool approval denied" : part.state === "cancelled" ? "Tool call cancelled" : "Tool call failed") },
148
144
  isError: true
149
145
  };
150
- case "output-denied": return {
146
+ default: return part.output === void 0 ? toolCall : {
151
147
  ...toolCall,
152
- result: { error: part.approval?.reason ?? "Tool approval denied" },
153
- isError: true
148
+ result: part.output,
149
+ isPreliminary: true
154
150
  };
155
- default: return toolCall;
156
151
  }
157
152
  };
158
- const convertAuthorizationPart = (part) => ({
159
- type: "data",
160
- name: "authorization",
161
- data: {
162
- state: part.state,
163
- name: part.name,
164
- ...part.displayName !== void 0 && { displayName: part.displayName },
165
- ...part.description !== void 0 && { description: part.description },
166
- ...part.authorization?.url !== void 0 && httpUrlPattern.test(part.authorization.url) && { url: part.authorization.url },
167
- ...part.authorization?.userCode !== void 0 && { userCode: part.authorization.userCode },
168
- ...part.authorization?.instructions !== void 0 && { instructions: part.authorization.instructions },
169
- ...part.authorization?.expiresAt !== void 0 && { expiresAt: part.authorization.expiresAt },
170
- ...part.outcome !== void 0 && { outcome: part.outcome },
171
- ...part.reason !== void 0 && { reason: part.reason }
153
+ const toAuthorizationOutcome = (outcome) => {
154
+ switch (outcome) {
155
+ case "accepted": return "authorized";
156
+ case "declined":
157
+ case "invalid": return "declined";
158
+ case "expired": return "timed-out";
159
+ case "failed": return "failed";
160
+ default: return "cancelled";
172
161
  }
173
- });
162
+ };
163
+ const convertAuthorizationPart = (interaction) => {
164
+ const signIn = interaction.request.signIn;
165
+ return {
166
+ type: "data",
167
+ name: "authorization",
168
+ data: {
169
+ state: interaction.status === "open" ? "required" : "completed",
170
+ name: signIn?.name ?? interaction.request.title ?? "sign-in",
171
+ ...signIn?.displayName !== void 0 && { displayName: signIn.displayName },
172
+ description: interaction.request.prompt,
173
+ ...signIn?.url !== void 0 && httpUrlPattern.test(signIn.url) && { url: signIn.url },
174
+ ...signIn?.userCode !== void 0 && { userCode: signIn.userCode },
175
+ ...signIn?.instructions !== void 0 && { instructions: signIn.instructions },
176
+ ...signIn?.expiresAt !== void 0 && { expiresAt: signIn.expiresAt },
177
+ ...interaction.outcome !== void 0 && { outcome: toAuthorizationOutcome(interaction.outcome) },
178
+ ...interaction.reason !== void 0 && { reason: interaction.reason }
179
+ }
180
+ };
181
+ };
182
+ const convertInteractionPart = (interaction) => {
183
+ if (interaction.request.kind === "sign-in") return convertAuthorizationPart(interaction);
184
+ const approval = toApproval(interaction);
185
+ return {
186
+ type: "tool-call",
187
+ toolCallId: interaction.interactionId,
188
+ toolName: interaction.call?.name ?? interaction.request.kind,
189
+ args: toJsonObject(interaction.call?.input),
190
+ argsText: stringifyArgs(interaction.call?.input),
191
+ approval,
192
+ providerMetadata: toToolProviderMetadata(interaction),
193
+ ...interaction.status === "settled" && {
194
+ result: interaction.answer ?? {
195
+ outcome: interaction.outcome,
196
+ reason: interaction.reason
197
+ },
198
+ ...interaction.outcome !== "accepted" && { isError: true }
199
+ }
200
+ };
201
+ };
174
202
  const convertFilePart = (part) => {
175
203
  if (part.url === void 0) return null;
176
204
  return {
@@ -193,9 +221,8 @@ const convertAssistantPart = (part) => {
193
221
  ...status && { status }
194
222
  };
195
223
  }
196
- case "step-start": return null;
197
- case "dynamic-tool": return convertDynamicToolPart(part);
198
- case "authorization": return convertAuthorizationPart(part);
224
+ case "tool": return convertToolPart(part);
225
+ case "interaction": return convertInteractionPart(part);
199
226
  case "file": return convertFilePart(part);
200
227
  default: return null;
201
228
  }
@@ -265,40 +292,43 @@ const convertEveMessageWithInterruptions = (message, index, messages, options, i
265
292
  };
266
293
  return fromThreadMessageLike(like, message.id, toMessageStatus(message, index, messages, options, interruptedTurns));
267
294
  };
268
- const isTurnInterruption = (event) => event.type === "turn.failed" || event.type === "turn.cancelled";
269
- /**
270
- * Collects the `turn.failed` and `turn.cancelled` events of an append-only
271
- * event log, scanning only the events appended since the cached call. The
272
- * returned array keeps its identity until a new interruption arrives.
273
- */
295
+ const isTurnInterruption = (event) => event.type === "turn.settled" && event.data.outcome !== "completed" || event.type === "session.ended" && event.data.outcome === "failed";
274
296
  const collectInterruptedTurnEvents = (events, cache) => {
275
297
  if (events === cache.lastEvents) return cache.interruptions;
276
298
  const scanned = cache.lastEvents;
277
299
  const resumesScan = scanned.length === 0 || events[scanned.length - 1] === scanned[scanned.length - 1];
278
- let interruptions = resumesScan ? cache.interruptions : [];
300
+ const interruptions = resumesScan ? cache.interruptions : [];
301
+ let nextInterruptions;
279
302
  for (let i = resumesScan ? scanned.length : 0; i < events.length; i++) {
280
303
  const event = events[i];
281
- if (isTurnInterruption(event)) interruptions = [...interruptions, event];
304
+ if (isTurnInterruption(event)) {
305
+ nextInterruptions ??= [...interruptions];
306
+ nextInterruptions.push(event);
307
+ }
282
308
  }
283
309
  cache.lastEvents = events;
284
- cache.interruptions = interruptions;
285
- return interruptions;
310
+ cache.interruptions = nextInterruptions ?? interruptions;
311
+ return cache.interruptions;
286
312
  };
287
313
  const getInterruptedTurns = (events) => {
288
314
  const interrupted = /* @__PURE__ */ new Map();
289
- for (const event of events ?? []) if (event.type === "turn.failed") interrupted.set(event.data.turnId, {
315
+ const sessionFailures = /* @__PURE__ */ new Map();
316
+ for (const event of events ?? []) if (event.type === "turn.settled" && event.data.outcome === "failed") interrupted.set(event.data.turnId, {
290
317
  type: "incomplete",
291
318
  reason: "error",
292
- error: {
293
- code: event.data.code,
294
- message: event.data.message
295
- }
319
+ ...event.data.error && { error: toAssistantError(event.data.error) }
296
320
  });
297
- else if (event.type === "turn.cancelled") interrupted.set(event.data.turnId, ASSISTANT_CANCELLED_STATUS);
321
+ else if (event.type === "turn.settled" && event.data.outcome === "cancelled") interrupted.set(event.data.turnId, ASSISTANT_CANCELLED_STATUS);
322
+ else if (event.type === "session.ended" && event.data.outcome === "failed" && event.data.cause && "turnId" in event.data.cause) sessionFailures.set(event.data.cause.turnId, {
323
+ type: "incomplete",
324
+ reason: "error",
325
+ ...event.data.error && { error: toAssistantError(event.data.error) }
326
+ });
327
+ for (const [turnId, status] of sessionFailures) interrupted.set(turnId, status);
298
328
  return interrupted;
299
329
  };
300
330
  /**
301
- * Converts the full Eve message data object into assistant-ui thread messages.
331
+ * Converts Eve's conversation messages into assistant-ui thread messages.
302
332
  */
303
333
  const convertEveMessages = (data, options = {}) => {
304
334
  const interruptedTurns = getInterruptedTurns(options.events);
@@ -358,63 +388,62 @@ const getEveMessageContent = (message) => {
358
388
  return parts;
359
389
  };
360
390
  /**
361
- * Finds the pending input request answered by the given approval id, so the
362
- * response mapping can honor the request's display mode.
391
+ * Finds the request of the interaction answered by the given approval id, so
392
+ * the answer mapping can honor the request's display mode.
363
393
  */
364
394
  const findEveInputRequest = (data, approvalId) => {
365
- for (const message of data.messages) for (const part of message.parts) if (part.type === "dynamic-tool" && part.approval?.id === approvalId) return part.toolMetadata?.eve?.inputRequest;
395
+ for (const message of data.messages) for (const part of message.parts) {
396
+ const interaction = part.type === "interaction" ? part : part.type === "tool" ? part.interaction : void 0;
397
+ if (interaction?.interactionId === approvalId) return interaction.request;
398
+ }
366
399
  };
367
400
  /**
368
- * Converts an assistant-ui tool approval response into an Eve input response.
401
+ * Converts an assistant-ui tool approval response into an Eve `Answer`.
369
402
  *
370
- * When `inputRequest` is known, every returned response carries either an
371
- * option the request declares or a free-form answer. A literal option match
372
- * wins, then the `"approve"` / `"cancel"` option the response's boolean decision
403
+ * When `inputRequest` is known, every returned answer carries an option the
404
+ * request declares, the literal `"approve"` / `"deny"` for an approval that
405
+ * declares no options, or a free-form answer. A literal option match wins,
406
+ * then the `"approve"` / `"deny"` option the response's boolean decision
373
407
  * names, then the response's `text` (or its `reason`, for a caller that
374
408
  * predates the first-class answer field) when the request takes a free-form
375
409
  * answer (`display: "text"`, `allowFreeform`, or no options at all, and never
376
410
  * `display: "confirmation"`) and the response is not a refusal.
377
- * A one-argument call, or a part with no request, still forwards a caller
378
- * `optionId` as it always has, including ids the request never declared.
379
411
  *
380
412
  * A response the known request cannot record as the answer it asked for throws
381
- * instead of being submitted, because eve resolves a request the moment any
382
- * response for it arrives, and an empty one is recorded as an answer with no
383
- * content. Not sending leaves the request pending, so the caller can retry or
413
+ * instead of being submitted, because Eve resolves an interaction the moment
414
+ * any answer for it arrives, and an empty one is recorded as an answer with no
415
+ * content. Not sending leaves the interaction open, so the caller can retry or
384
416
  * the user can answer it as an ordinary message.
385
417
  *
386
- * With no request to map against — a one-argument call, or a part carrying no
387
- * `toolMetadata.eve.inputRequest` — the response maps as it always has, to the
388
- * literal `"approve"` / `"cancel"` option every eve approval declares. Guessing
389
- * a display mode is what this mapper stopped doing; an unknown request is not
390
- * a guess about one.
418
+ * With no request to map against, a one-argument call forwards a caller
419
+ * `optionId` as is and otherwise maps the decision to the literal `"approve"` /
420
+ * `"deny"` option every Eve approval declares.
391
421
  */
392
422
  const toEveInputResponse = (response, inputRequest) => {
393
- const requestId = response.approvalId;
423
+ const interactionId = response.approvalId;
394
424
  const options = inputRequest?.options;
395
425
  const text = response.text ?? response.reason;
396
426
  if (response.optionId !== void 0) {
397
427
  if (!inputRequest || options?.some((option) => option.id === response.optionId)) return {
398
- requestId,
428
+ interactionId,
399
429
  optionId: response.optionId,
400
430
  ...text && { text }
401
431
  };
402
- throw new Error(`Eve input request "${requestId}" has no option with id "${response.optionId}"; respond with one of: ${options?.length ? options.map((option) => option.id).join(", ") : "(the request carries no options)"}`);
432
+ throw new Error(`Eve interaction "${interactionId}" has no option with id "${response.optionId}"; respond with one of: ${options?.length ? options.map((option) => option.id).join(", ") : "(the request carries no options)"}`);
403
433
  }
404
- const decisionOptionId = response.approved ? "approve" : "cancel";
405
- if (!inputRequest || inputRequest.kind === "tool-approval" && !options?.length || options?.some((option) => option.id === decisionOptionId)) return {
406
- requestId,
434
+ const decisionOptionId = response.approved ? APPROVE_OPTION_ID : DENY_OPTION_ID;
435
+ if (!inputRequest || inputRequest.kind === "approval" && !options?.length || options?.some((option) => option.id === decisionOptionId)) return {
436
+ interactionId,
407
437
  optionId: decisionOptionId,
408
438
  ...text && { text }
409
439
  };
410
440
  const acceptsText = acceptsFreeformAnswer(inputRequest);
411
441
  if (acceptsText && text && response.approved !== false) return {
412
- requestId,
442
+ interactionId,
413
443
  text
414
444
  };
415
- throw new Error(`Eve input request "${requestId}" (${inputRequest.prompt}) was not answered by this response; ${options?.length ? `respond with one of: ${options.map((option) => option.id).join(", ")}` : !acceptsText ? "the request declares no options to respond with" : response.approved === false ? "a refusal carries no answer for a free-form request; resend with `approved` unset or true and the answer in the response text" : "pass the answer as the response text, because the request takes a free-form answer"}`);
445
+ throw new Error(`Eve interaction "${interactionId}" (${inputRequest.prompt}) was not answered by this response; ${options?.length ? `respond with one of: ${options.map((option) => option.id).join(", ")}` : !acceptsText ? "the request declares no options to respond with" : response.approved === false ? "a refusal carries no answer for a free-form request; resend with `approved` unset or true and the answer in the response text" : "pass the answer as the response text, because the request takes a free-form answer"}`);
416
446
  };
417
- //#endregion
418
447
  export { collectInterruptedTurnEvents, convertEveMessage, convertEveMessages, findEveInputRequest, getEveMessageContent, toEveInputResponse };
419
448
 
420
449
  //# sourceMappingURL=convertEveMessages.js.map