@truefoundry/assistant-ui-runtime 0.1.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +567 -0
  2. package/dist/index.d.ts +300 -0
  3. package/dist/index.js +4440 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +70 -0
  6. package/src/agentSessionModule.d.ts +25 -0
  7. package/src/agentSpec.ts +44 -0
  8. package/src/askUserQuestion.ts +37 -0
  9. package/src/attachmentAdapter.test.ts +56 -0
  10. package/src/attachmentAdapter.ts +58 -0
  11. package/src/bindDraftAgentSession.test.ts +55 -0
  12. package/src/bindDraftAgentSession.ts +66 -0
  13. package/src/buildEditedUserMessageContent.test.ts +61 -0
  14. package/src/collectPending.test.ts +69 -0
  15. package/src/collectPending.ts +165 -0
  16. package/src/constants.ts +2 -0
  17. package/src/convertTurnMessages.test.ts +1991 -0
  18. package/src/convertTurnMessages.ts +1251 -0
  19. package/src/createSubAgent.ts +8 -0
  20. package/src/draftAgentConfig.test.ts +88 -0
  21. package/src/draftSessionBridge.ts +28 -0
  22. package/src/extractTurnUserText.ts +21 -0
  23. package/src/foldPeerThreads.test.ts +386 -0
  24. package/src/foldPeerThreads.ts +587 -0
  25. package/src/hooks.ts +123 -0
  26. package/src/index.ts +68 -0
  27. package/src/lastUserMessageText.ts +19 -0
  28. package/src/loadSessionSnapshot.test.ts +57 -0
  29. package/src/loadSessionSnapshot.ts +35 -0
  30. package/src/mcpAuth.ts +44 -0
  31. package/src/messageCustomMetadata.ts +37 -0
  32. package/src/modelMessageContent.ts +135 -0
  33. package/src/modelMessageImageContent.test.ts +109 -0
  34. package/src/modelMessageImageContent.ts +193 -0
  35. package/src/requiredActionInputs.test.ts +394 -0
  36. package/src/requiredActionInputs.ts +65 -0
  37. package/src/requiredActionsFromActiveUpdate.test.ts +95 -0
  38. package/src/sessionListStartTimestamp.ts +6 -0
  39. package/src/sessionSnapshot.ts +107 -0
  40. package/src/sessions.ts +36 -0
  41. package/src/streamTurn.test.ts +243 -0
  42. package/src/streamTurn.ts +119 -0
  43. package/src/toolApproval.test.ts +137 -0
  44. package/src/toolApproval.ts +459 -0
  45. package/src/toolResponse.test.ts +136 -0
  46. package/src/toolResponse.ts +427 -0
  47. package/src/truefoundryDraftThreadListAdapter.test.ts +140 -0
  48. package/src/truefoundryDraftThreadListAdapter.ts +63 -0
  49. package/src/truefoundryExtras.ts +45 -0
  50. package/src/truefoundryThreadListAdapter.test.ts +103 -0
  51. package/src/truefoundryThreadListAdapter.ts +59 -0
  52. package/src/turnEventHelpers.ts +98 -0
  53. package/src/turnStreamUpdate.ts +11 -0
  54. package/src/types.ts +92 -0
  55. package/src/useDraftAgentSpec.ts +173 -0
  56. package/src/useTrueFoundryAgentMessages.test.tsx +723 -0
  57. package/src/useTrueFoundryAgentMessages.ts +757 -0
  58. package/src/useTrueFoundryAgentRuntime.ts +268 -0
@@ -0,0 +1,165 @@
1
+ import type { ThreadAssistantMessagePart, ThreadMessage } from "@assistant-ui/core";
2
+ import type { McpAuthRequiredEvent } from "truefoundry-gateway-sdk/agents";
3
+
4
+ import { ROOT_THREAD_ID } from "./constants.js";
5
+ import type { TrueFoundryMessageCustomMetadata } from "./messageCustomMetadata.js";
6
+ import {
7
+ getToolApprovalThreadId,
8
+ hasPendingToolApproval,
9
+ } from "./toolApproval.js";
10
+ import {
11
+ getToolResponseThreadId,
12
+ hasPendingToolResponse,
13
+ type AskUserQuestionInterruptPayload,
14
+ } from "./toolResponse.js";
15
+
16
+ export type PendingApproval = {
17
+ approvalId: string;
18
+ threadId: string;
19
+ toolName: string;
20
+ args: Record<string, unknown>;
21
+ argsText: string;
22
+ };
23
+
24
+ export type PendingToolResponse = {
25
+ toolCallId: string;
26
+ threadId: string;
27
+ toolName: string;
28
+ args: Record<string, unknown>;
29
+ argsText: string;
30
+ question?: string;
31
+ options?: string[];
32
+ };
33
+
34
+ type ToolCallPart = Extract<
35
+ ThreadMessage["content"][number],
36
+ { type: "tool-call" }
37
+ >;
38
+
39
+ function walkToolCallParts(
40
+ content: readonly ThreadAssistantMessagePart[],
41
+ visit: (part: ToolCallPart, threadId: string) => void,
42
+ threadId: string,
43
+ ): void {
44
+ for (const part of content) {
45
+ if (part.type !== "tool-call") {
46
+ continue;
47
+ }
48
+ visit(part, threadId);
49
+ if (part.messages == null) {
50
+ continue;
51
+ }
52
+ for (const message of part.messages) {
53
+ if (message.role !== "assistant") {
54
+ continue;
55
+ }
56
+ const nestedThreadId =
57
+ getToolApprovalThreadId(message) ??
58
+ getToolResponseThreadId(message) ??
59
+ threadId;
60
+ walkToolCallParts(message.content, visit, nestedThreadId);
61
+ }
62
+ }
63
+ }
64
+
65
+ export function collectPendingApprovals(
66
+ messages: readonly ThreadMessage[],
67
+ ): PendingApproval[] {
68
+ const pending: PendingApproval[] = [];
69
+
70
+ for (const message of messages) {
71
+ if (message.role !== "assistant") {
72
+ continue;
73
+ }
74
+ const rootThreadId = getToolApprovalThreadId(message) ?? ROOT_THREAD_ID;
75
+ walkToolCallParts(message.content, (part, threadId) => {
76
+ if (!hasPendingToolApproval(part.approval)) {
77
+ return;
78
+ }
79
+ pending.push({
80
+ approvalId: part.approval!.id,
81
+ threadId,
82
+ toolName: part.toolName,
83
+ args: { ...part.args },
84
+ argsText: part.argsText,
85
+ });
86
+ }, rootThreadId);
87
+ }
88
+
89
+ return pending;
90
+ }
91
+
92
+ export function collectPendingToolResponses(
93
+ messages: readonly ThreadMessage[],
94
+ ): PendingToolResponse[] {
95
+ const pending: PendingToolResponse[] = [];
96
+
97
+ for (const message of messages) {
98
+ if (message.role !== "assistant") {
99
+ continue;
100
+ }
101
+ const rootThreadId = getToolResponseThreadId(message) ?? ROOT_THREAD_ID;
102
+ walkToolCallParts(message.content, (part, threadId) => {
103
+ if (!hasPendingToolResponse(part)) {
104
+ return;
105
+ }
106
+ const payload = part.interrupt?.payload as
107
+ | AskUserQuestionInterruptPayload
108
+ | undefined;
109
+ pending.push({
110
+ toolCallId: part.toolCallId,
111
+ threadId,
112
+ toolName: part.toolName,
113
+ args: { ...part.args },
114
+ argsText: part.argsText,
115
+ ...(payload?.question != null ? { question: payload.question } : {}),
116
+ ...(payload?.options != null ? { options: payload.options } : {}),
117
+ });
118
+ }, rootThreadId);
119
+ }
120
+
121
+ return pending;
122
+ }
123
+
124
+ export function derivePendingMcpAuth(
125
+ messages: readonly ThreadMessage[],
126
+ ): { mcpServers: McpAuthRequiredEvent["mcpServers"] } | null {
127
+ for (let i = messages.length - 1; i >= 0; i--) {
128
+ const message = messages[i];
129
+ if (message?.role !== "assistant") {
130
+ continue;
131
+ }
132
+ if (message.status?.type !== "requires-action") {
133
+ continue;
134
+ }
135
+ const custom = message.metadata.custom as TrueFoundryMessageCustomMetadata;
136
+ if (custom.pendingMcpAuth !== true) {
137
+ continue;
138
+ }
139
+ const servers = custom.mcpServers;
140
+ if (!Array.isArray(servers)) {
141
+ return { mcpServers: [] };
142
+ }
143
+ return { mcpServers: servers };
144
+ }
145
+ return null;
146
+ }
147
+
148
+ /**
149
+ * Most recent `sandboxId` observed anywhere in the conversation, scanning backward.
150
+ * Unlike `derivePendingMcpAuth`, this isn't gated on message status: once a sandbox
151
+ * is created it stays valid for the rest of the session, not just the paused message.
152
+ */
153
+ export function deriveSandboxId(messages: readonly ThreadMessage[]): string | undefined {
154
+ for (let i = messages.length - 1; i >= 0; i--) {
155
+ const message = messages[i];
156
+ if (message?.role !== "assistant") {
157
+ continue;
158
+ }
159
+ const custom = message.metadata.custom as TrueFoundryMessageCustomMetadata;
160
+ if (typeof custom.sandboxId === "string") {
161
+ return custom.sandboxId;
162
+ }
163
+ }
164
+ return undefined;
165
+ }
@@ -0,0 +1,2 @@
1
+ /** Gateway root thread id — always the literal `"main"`. */
2
+ export const ROOT_THREAD_ID = "main";