@truefoundry/assistant-ui-runtime 0.1.5 → 0.1.6

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 (70) hide show
  1. package/README.md +198 -401
  2. package/dist/chunk-3A2EPLQG.js +93 -0
  3. package/dist/chunk-3A2EPLQG.js.map +1 -0
  4. package/dist/chunk-Q2SHKMLM.js +270 -0
  5. package/dist/chunk-Q2SHKMLM.js.map +1 -0
  6. package/dist/index.d.ts +24 -29
  7. package/dist/index.js +264 -237
  8. package/dist/index.js.map +1 -1
  9. package/dist/plugins/truefoundry-agent-server-adapter/index.d.ts +162 -0
  10. package/dist/plugins/truefoundry-agent-server-adapter/index.js +19 -0
  11. package/dist/plugins/truefoundry-agent-server-adapter/index.js.map +1 -0
  12. package/dist/server/index.d.ts +17 -0
  13. package/dist/server/index.js +9 -0
  14. package/dist/server/index.js.map +1 -0
  15. package/dist/types-BfiFf8O1.d.ts +468 -0
  16. package/package.json +19 -6
  17. package/src/askUserQuestion.ts +3 -3
  18. package/src/buildEditedUserMessageContent.test.ts +2 -2
  19. package/src/collectPending.ts +1 -1
  20. package/src/convertTurnMessages.test.ts +141 -196
  21. package/src/convertTurnMessages.ts +130 -76
  22. package/src/createSubAgent.ts +1 -1
  23. package/src/draftAgentConfig.test.ts +26 -29
  24. package/src/extractTurnUserText.ts +1 -1
  25. package/src/foldPeerThreads.test.ts +1 -1
  26. package/src/foldPeerThreads.ts +3 -2
  27. package/src/index.ts +80 -4
  28. package/src/listPages.ts +21 -0
  29. package/src/loadSessionSnapshot.test.ts +9 -8
  30. package/src/loadSessionSnapshot.ts +9 -14
  31. package/src/mcpAuth.ts +6 -3
  32. package/src/messageCustomMetadata.ts +1 -1
  33. package/src/modelMessageContent.ts +1 -1
  34. package/src/modelMessageImageContent.test.ts +1 -1
  35. package/src/modelMessageImageContent.ts +7 -6
  36. package/src/plugins/truefoundry-agent-server-adapter/README.md +178 -0
  37. package/src/plugins/truefoundry-agent-server-adapter/guards.test.ts +113 -0
  38. package/src/plugins/truefoundry-agent-server-adapter/guards.ts +130 -0
  39. package/src/plugins/truefoundry-agent-server-adapter/index.ts +359 -0
  40. package/src/plugins/truefoundry-agent-server-adapter/types.ts +135 -0
  41. package/src/plugins/truefoundry-agent-server-adapter/types.typecheck.ts +164 -0
  42. package/src/private/agentSpec.ts +8 -3
  43. package/src/private/draftSessionBridge.ts +14 -13
  44. package/src/private/truefoundryDraftThreadListAdapter.test.ts +44 -49
  45. package/src/private/truefoundryDraftThreadListAdapter.ts +22 -16
  46. package/src/requiredActionInputs.ts +1 -1
  47. package/src/requiredActionsFromActiveUpdate.test.ts +1 -1
  48. package/src/server/eventUtils.ts +120 -0
  49. package/src/server/events.ts +246 -0
  50. package/src/server/index.ts +66 -0
  51. package/src/server/types.ts +319 -0
  52. package/src/sessionSnapshot.ts +1 -1
  53. package/src/sessions.ts +5 -21
  54. package/src/streamTurn.test.ts +172 -155
  55. package/src/streamTurn.ts +51 -48
  56. package/src/toolApproval.ts +4 -4
  57. package/src/toolResponse.ts +4 -4
  58. package/src/truefoundryExtras.ts +1 -1
  59. package/src/truefoundryOwnedSessionsThreadListAdapter.test.ts +26 -29
  60. package/src/truefoundryOwnedSessionsThreadListAdapter.ts +18 -23
  61. package/src/truefoundryThreadListAdapter.test.ts +16 -18
  62. package/src/truefoundryThreadListAdapter.ts +7 -7
  63. package/src/turnEventHelpers.ts +1 -1
  64. package/src/types.ts +2 -16
  65. package/src/useTrueFoundryAgentMessages.test.tsx +38 -70
  66. package/src/useTrueFoundryAgentMessages.ts +32 -44
  67. package/src/useTrueFoundryAgentRuntime.ts +11 -28
  68. package/src/private/bindDraftAgentSession.test.ts +0 -54
  69. package/src/private/bindDraftAgentSession.ts +0 -28
  70. package/src/private/getGatewayFromPrivateClient.ts +0 -13
package/src/index.ts CHANGED
@@ -12,15 +12,18 @@ export {
12
12
  } from "./convertTurnMessages.js";
13
13
  export type { ConvertTurnsResult, UserMessageContent } from "./convertTurnMessages.js";
14
14
  export { ROOT_THREAD_ID } from "./constants.js";
15
- export type { UseTrueFoundryAgentRuntimeOptions, NamedAgentConfig, DraftAgentConfig, TrueFoundryAgentConfig } from "./types.js";
15
+ export type {
16
+ UseTrueFoundryAgentRuntimeOptions,
17
+ NamedAgentConfig,
18
+ DraftAgentConfig,
19
+ TrueFoundryAgentConfig,
20
+ } from "./types.js";
16
21
  export type { AgentSpec, AgentSpecUpdate, DraftSession } from "./private/agentSpec.js";
17
22
  export { mergeAgentSpec, draftSessionTitle } from "./private/agentSpec.js";
18
23
  export { createTrueFoundryDraftThreadListAdapter } from "./private/truefoundryDraftThreadListAdapter.js";
19
24
  export { createTrueFoundryOwnedSessionsThreadListAdapter } from "./truefoundryOwnedSessionsThreadListAdapter.js";
20
25
  export { createDraftSessionBridge } from "./private/draftSessionBridge.js";
21
26
  export type { DraftSessionBridge } from "./private/draftSessionBridge.js";
22
- export { PrivateAgentSessionClient } from "truefoundry-gateway-sdk/agents/private";
23
- export type { AgentDraftSession } from "truefoundry-gateway-sdk/agents/private";
24
27
  export {
25
28
  useTrueFoundryAgentSpec,
26
29
  useTrueFoundryUpdateAgentSpec,
@@ -35,7 +38,6 @@ export type {
35
38
  ToolApprovalMessageCustomMetadata,
36
39
  ToolResponseMessageCustomMetadata,
37
40
  } from "./messageCustomMetadata.js";
38
- export type { SandboxCreatedEvent } from "truefoundry-gateway-sdk/agents";
39
41
  export type { PendingApproval, PendingToolResponse } from "./collectPending.js";
40
42
  export type { TrueFoundryRuntimeExtras } from "./truefoundryExtras.js";
41
43
  export { trueFoundryExtras } from "./truefoundryExtras.js";
@@ -71,3 +73,77 @@ export {
71
73
  export { createTrueFoundryThreadListAdapter } from "./truefoundryThreadListAdapter.js";
72
74
  export { getSession } from "./sessions.js";
73
75
  export { trueFoundryAttachmentAdapter } from "./attachmentAdapter.js";
76
+
77
+ export type {
78
+ AgentChatServer,
79
+ AgentBuilderServer,
80
+ Session,
81
+ Turn,
82
+ TurnState,
83
+ TurnStateDone,
84
+ TurnInputItem,
85
+ AgentSpec as ServerAgentSpec,
86
+ ListResult,
87
+ CreateSessionRequest,
88
+ UpdateSessionRequest,
89
+ ListSessionsParams,
90
+ UserMessage,
91
+ UserToolApprovalEvent,
92
+ UserToolResponseEvent,
93
+ PreviousTurnIdInput,
94
+ } from "./server/index.js";
95
+ export type {
96
+ SandboxCreatedEvent,
97
+ McpAuthRequiredEvent,
98
+ ModelMessageEvent,
99
+ TurnEvent,
100
+ TurnStreamingEvent,
101
+ TurnStreamData,
102
+ SessionEventItem,
103
+ ToolCall,
104
+ ThreadCreatedEvent,
105
+ ToolApprovalRequiredEvent,
106
+ ToolResponseRequiredEvent,
107
+ } from "./server/index.js";
108
+ export { isEventDelta, mergeEventDelta } from "./server/index.js";
109
+
110
+ // ---------------------------------------------------------------------------
111
+ // Plugin: truefoundry-agent-server-adapter
112
+ // ---------------------------------------------------------------------------
113
+
114
+ export {
115
+ createTrueFoundryChatServer,
116
+ type CreateTrueFoundryChatServerOptions,
117
+ type TrueFoundryChatServer,
118
+ type TfyAgentSpec,
119
+ type TfySkillMount,
120
+ type TfyMcpServerMount,
121
+ type TfyModelParams,
122
+ type TfyRuntimeConfig,
123
+ type TfyResponseFormat,
124
+ type TfySubject,
125
+ type ToolsSelectorItem,
126
+ type ToolsSelectorTag,
127
+ type RequireApprovalToolSelectorItem,
128
+ type RequireApprovalToolsSelectorTag,
129
+ type TfyTurn,
130
+ type TfyTurnState,
131
+ type TfyTurnCancelledReason,
132
+ type TfyTurnStateDoneOutput,
133
+ type TfySession,
134
+ type TfyCreateSessionRequest,
135
+ type TfyListSessionsParams,
136
+ type TfyToolInfo,
137
+ type TfySystemToolInfo,
138
+ type TfyMcpToolInfo,
139
+ type TfyModelMessageUsage,
140
+ type TfyFinishReason,
141
+ type TfyThreadState,
142
+ type TfyMcpServerInitInfo,
143
+ isTfyToolInfo,
144
+ isTfySystemToolInfo,
145
+ isTfyMcpToolInfo,
146
+ getTfyUsage,
147
+ getTfyThreadState,
148
+ getTfyMcpInitServers,
149
+ } from "./plugins/truefoundry-agent-server-adapter/index.js";
@@ -0,0 +1,21 @@
1
+ import type { ListResult } from "./server/types.js";
2
+
3
+ /**
4
+ * Drains all pages of a token-paginated list into a single array.
5
+ * Newest-first APIs should reverse after calling this.
6
+ */
7
+ export async function drainListPages<T>(
8
+ fetchPage: (pageToken?: string) => Promise<ListResult<T>>,
9
+ ): Promise<T[]> {
10
+ const items: T[] = [];
11
+ let pageToken: string | undefined;
12
+ for (;;) {
13
+ const page = await fetchPage(pageToken);
14
+ items.push(...page.data);
15
+ if (page.nextPageToken == null || page.nextPageToken === "") {
16
+ break;
17
+ }
18
+ pageToken = page.nextPageToken;
19
+ }
20
+ return items;
21
+ }
@@ -1,4 +1,4 @@
1
- import type { AgentSessionClient } from "truefoundry-gateway-sdk/agents";
1
+ import type { AgentChatServer } from "./server/index.js";
2
2
  import { beforeEach, describe, expect, it, vi } from "vitest";
3
3
 
4
4
  import { createEmptySessionSnapshot } from "./sessionSnapshot.js";
@@ -19,7 +19,7 @@ import { buildSnapshotFromSessionEvents } from "./convertTurnMessages.js";
19
19
  import { loadSessionSnapshot } from "./loadSessionSnapshot.js";
20
20
  import { getSession } from "./sessions.js";
21
21
 
22
- const mockClient = {} as AgentSessionClient;
22
+ const mockServer = {} as AgentChatServer;
23
23
 
24
24
  describe("loadSessionSnapshot", () => {
25
25
  beforeEach(() => {
@@ -33,8 +33,8 @@ describe("loadSessionSnapshot", () => {
33
33
  );
34
34
 
35
35
  const [first, second] = await Promise.all([
36
- loadSessionSnapshot(mockClient, "session-1"),
37
- loadSessionSnapshot(mockClient, "session-1"),
36
+ loadSessionSnapshot(mockServer, "session-1"),
37
+ loadSessionSnapshot(mockServer, "session-1"),
38
38
  ]);
39
39
 
40
40
  expect(first).toBe(second);
@@ -48,8 +48,8 @@ describe("loadSessionSnapshot", () => {
48
48
  createEmptySessionSnapshot(),
49
49
  );
50
50
 
51
- await loadSessionSnapshot(mockClient, "session-1");
52
- await loadSessionSnapshot(mockClient, "session-1");
51
+ await loadSessionSnapshot(mockServer, "session-1");
52
+ await loadSessionSnapshot(mockServer, "session-1");
53
53
 
54
54
  expect(getSession).toHaveBeenCalledTimes(2);
55
55
  expect(buildSnapshotFromSessionEvents).toHaveBeenCalledTimes(2);
@@ -62,10 +62,11 @@ describe("loadSessionSnapshot", () => {
62
62
  );
63
63
 
64
64
  const onProgress = vi.fn();
65
- await loadSessionSnapshot(mockClient, "session-1", undefined, onProgress);
65
+ await loadSessionSnapshot(mockServer, "session-1", onProgress);
66
66
 
67
67
  expect(buildSnapshotFromSessionEvents).toHaveBeenCalledWith(
68
- {},
68
+ mockServer,
69
+ "session-1",
69
70
  onProgress,
70
71
  );
71
72
  });
@@ -1,7 +1,7 @@
1
- import type { AgentSessionClient } from "truefoundry-gateway-sdk/agents";
1
+ import type { AgentChatServer } from "./server/types.js";
2
2
 
3
3
  import { buildSnapshotFromSessionEvents } from "./convertTurnMessages.js";
4
- import { getSession, type GetSessionOptions } from "./sessions.js";
4
+ import { getSession } from "./sessions.js";
5
5
  import type { SessionSnapshot } from "./sessionSnapshot.js";
6
6
 
7
7
  const inflightBySessionId = new Map<string, Promise<SessionSnapshot>>();
@@ -15,25 +15,20 @@ const inflightBySessionId = new Map<string, Promise<SessionSnapshot>>();
15
15
  * can update the UI progressively while history is being processed.
16
16
  */
17
17
  export function loadSessionSnapshot(
18
- client: AgentSessionClient,
18
+ server: AgentChatServer,
19
19
  sessionId: string,
20
- sessionOptions?: GetSessionOptions,
21
20
  onProgress?: (snap: SessionSnapshot) => void,
22
21
  ): Promise<SessionSnapshot> {
23
- const cacheKey =
24
- sessionOptions?.privateClient != null ? `draft:${sessionId}` : sessionId;
25
- let inflight = inflightBySessionId.get(cacheKey);
22
+ let inflight = inflightBySessionId.get(sessionId);
26
23
  if (inflight == null) {
27
- inflight = getSession(client, sessionId, sessionOptions)
28
- .then((session) =>
29
- buildSnapshotFromSessionEvents(session, onProgress),
30
- )
24
+ inflight = getSession(server, sessionId)
25
+ .then(() => buildSnapshotFromSessionEvents(server, sessionId, onProgress))
31
26
  .finally(() => {
32
- if (inflightBySessionId.get(cacheKey) === inflight) {
33
- inflightBySessionId.delete(cacheKey);
27
+ if (inflightBySessionId.get(sessionId) === inflight) {
28
+ inflightBySessionId.delete(sessionId);
34
29
  }
35
30
  });
36
- inflightBySessionId.set(cacheKey, inflight);
31
+ inflightBySessionId.set(sessionId, inflight);
37
32
  }
38
33
  return inflight;
39
34
  }
package/src/mcpAuth.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { MessageStatus } from "@assistant-ui/core";
2
- import type { McpAuthRequiredEvent, TurnStateDone } from "truefoundry-gateway-sdk/agents";
2
+ import type { McpAuthRequiredEvent, TurnStateDone } from "./server/index.js";
3
3
 
4
4
  import type { AssistantContentPart } from "./modelMessageContent.js";
5
5
  import type { McpAuthMessageCustomMetadata } from "./messageCustomMetadata.js";
@@ -28,9 +28,12 @@ export function buildMcpAuthTextParts(
28
28
  export function findMcpAuthRequired(
29
29
  requiredActions: TurnStateDone["requiredActions"] | undefined,
30
30
  ): McpAuthRequiredEvent | undefined {
31
- return requiredActions?.find(
32
- (action): action is McpAuthRequiredEvent => action.type === "mcp.auth_required",
31
+ const found = requiredActions?.find(
32
+ (action) => action.type === "mcp.auth_required",
33
33
  );
34
+ return found?.type === "mcp.auth_required"
35
+ ? (found as McpAuthRequiredEvent)
36
+ : undefined;
34
37
  }
35
38
 
36
39
  export function mcpAuthAssistantStatus(): MessageStatus {
@@ -1,4 +1,4 @@
1
- import type { McpAuthRequiredEvent } from "truefoundry-gateway-sdk/agents";
1
+ import type { McpAuthRequiredEvent } from "./server/index.js";
2
2
 
3
3
  import type { SubAgentCustomMetadata } from "./foldPeerThreads.js";
4
4
  import { TOOL_APPROVAL_THREAD_ID_CUSTOM_KEY } from "./toolApproval.js";
@@ -1,5 +1,5 @@
1
1
  import type { ThreadAssistantMessagePart } from "@assistant-ui/core";
2
- import type { ModelMessageEvent } from "truefoundry-gateway-sdk/agents";
2
+ import type { ModelMessageEvent } from "./server/index.js";
3
3
  import type { PendingResponseRef } from "./foldPeerThreads.js";
4
4
  import { extractImagePartsFromModelMessage } from "./modelMessageImageContent.js";
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import type { ModelMessageEvent } from "truefoundry-gateway-sdk/agents";
2
+ import type { ModelMessageEvent } from "./server/index.js";
3
3
 
4
4
  import { buildAssistantContent } from "./modelMessageContent.js";
5
5
  import { mergeStreamEventDelta } from "./modelMessageImageContent.js";
@@ -1,10 +1,11 @@
1
1
  import type { CompleteAttachment } from "@assistant-ui/core";
2
- import type {
3
- ModelMessageEvent,
4
- TurnEvent,
5
- TurnStreamingEvent,
6
- } from "truefoundry-gateway-sdk/agents";
7
- import { isEventDelta, mergeEventDelta } from "truefoundry-gateway-sdk/agents";
2
+ import {
3
+ isEventDelta,
4
+ mergeEventDelta,
5
+ type ModelMessageEvent,
6
+ type TurnEvent,
7
+ type TurnStreamingEvent,
8
+ } from "./server/index.js";
8
9
 
9
10
  import type { AssistantContentPart } from "./modelMessageContent.js";
10
11
 
@@ -0,0 +1,178 @@
1
+ # truefoundry-agent-server-adapter
2
+
3
+ A gateway plugin that wraps [`truefoundry-gateway-sdk`](https://www.npmjs.com/package/truefoundry-gateway-sdk) into an [`AgentChatServer`](../../README.md#server-port-agentchatserver) for `@truefoundry/assistant-ui-runtime`.
4
+
5
+ Named vs draft session routing is internal — you pass `apiKey` / `baseUrl` (or pre-built clients) and get a flat server the runtime can call.
6
+
7
+ ### Used by the [runtime Quick start](../../README.md#quick-start)
8
+
9
+ ---
10
+
11
+ ## Table of contents
12
+
13
+ - [Installation](#installation)
14
+ - [Quick start](#quick-start)
15
+ - [`createTrueFoundryChatServer` options](#createtruefoundrychatserver-options)
16
+ - [Named vs draft sessions](#named-vs-draft-sessions)
17
+ - [Types & guards](#types--guards)
18
+ - [Extending `TfyAgentSpec`](#extending-tfyagentspec)
19
+ - [Exports](#exports)
20
+ - [License](#license)
21
+
22
+ ---
23
+
24
+ ## Installation
25
+
26
+ Shipped as a subpath of the runtime package (also re-exported from the main entry):
27
+
28
+ ```bash
29
+ npm install @truefoundry/assistant-ui-runtime truefoundry-gateway-sdk
30
+ # or
31
+ pnpm add @truefoundry/assistant-ui-runtime truefoundry-gateway-sdk
32
+ # or
33
+ yarn add @truefoundry/assistant-ui-runtime truefoundry-gateway-sdk
34
+ ```
35
+
36
+ `truefoundry-gateway-sdk` is an optional peer of the runtime — required only when using this plugin.
37
+
38
+ ---
39
+
40
+ ## Quick start
41
+
42
+ ```tsx
43
+ import { createTrueFoundryChatServer } from "@truefoundry/assistant-ui-runtime";
44
+ // Isolated import (no React):
45
+ // import { createTrueFoundryChatServer } from "@truefoundry/assistant-ui-runtime/plugins/truefoundry-agent-server-adapter";
46
+
47
+ const server = createTrueFoundryChatServer({
48
+ apiKey: process.env.TFY_API_KEY!,
49
+ baseUrl: process.env.TFY_GATEWAY_URL!,
50
+ });
51
+
52
+ // Pass `server` to useTrueFoundryAgentRuntime({ server, agentName })
53
+ ```
54
+
55
+ That returns a `TrueFoundryChatServer` implementing `AgentChatServer` with concrete `TfySession` / `TfyTurn` types.
56
+
57
+ ---
58
+
59
+ ## `createTrueFoundryChatServer` options
60
+
61
+ | Option | Type | Required | Description |
62
+ | ------ | ---- | -------- | ----------- |
63
+ | `apiKey` | `string` | ✅ | TrueFoundry API key |
64
+ | `baseUrl` | `string` | ✅ | Gateway base URL |
65
+ | `client` | `AgentSessionClient` | — | Override the named-session client (otherwise built from `apiKey` / `baseUrl`) |
66
+ | `privateClient` | `PrivateAgentSessionClient` | — | Override the draft/private client |
67
+ | `deleteSession` | `(req: { sessionId: string }) => Promise<void>` | — | Optional delete hook — not on the gateway SDK today; pass your own if needed |
68
+
69
+ ```tsx
70
+ const server = createTrueFoundryChatServer({
71
+ apiKey,
72
+ baseUrl,
73
+ // client, privateClient, deleteSession — optional overrides
74
+ });
75
+ ```
76
+
77
+ Escape hatch for hosts that still need raw gateway clients:
78
+
79
+ ```tsx
80
+ const { client, privateClient } = server.getGatewayClients();
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Named vs draft sessions
86
+
87
+ Routing is fully internal via an in-memory session-type cache populated by `createSession` / `listSessions`:
88
+
89
+ | Create with | Session kind | Client used |
90
+ | ----------- | ------------ | ----------- |
91
+ | `agentName` | Named (immutable) | `AgentSessionClient` |
92
+ | `agentSpec` | Draft (mutable) | `PrivateAgentSessionClient` |
93
+
94
+ `updateSession` is only allowed when `session.isMutable === true` (draft). Calling it on a named session throws.
95
+
96
+ > Ensure `createSession` or `listSessions` ran before `getSession` / turn methods for a given id — the adapter must have cached the session type.
97
+
98
+ ---
99
+
100
+ ## Types & guards
101
+
102
+ The plugin surfaces concrete gateway types for hosts that need them:
103
+
104
+ ```tsx
105
+ import type {
106
+ TfyAgentSpec,
107
+ TfySkillMount,
108
+ TfyMcpServerMount,
109
+ TfySession,
110
+ TfyTurn,
111
+ TfyTurnState,
112
+ TfyToolInfo,
113
+ } from "@truefoundry/assistant-ui-runtime";
114
+
115
+ import {
116
+ isTfyToolInfo,
117
+ isTfySystemToolInfo,
118
+ isTfyMcpToolInfo,
119
+ getTfyUsage,
120
+ getTfyThreadState,
121
+ getTfyMcpInitServers,
122
+ } from "@truefoundry/assistant-ui-runtime";
123
+ ```
124
+
125
+ Use the type guards to narrow event fields typed as `unknown` by the runtime.
126
+
127
+ ---
128
+
129
+ ## Extending `TfyAgentSpec`
130
+
131
+ Only the **spec** is generic. Session / turn / list-params stay as concrete `Tfy*` types. Host-added spec fields survive the round trip because the gateway SDK serializes with `unrecognizedObjectKeys: "passthrough"`:
132
+
133
+ ```tsx
134
+ import {
135
+ createTrueFoundryChatServer,
136
+ type TfyAgentSpec,
137
+ type TrueFoundryChatServer,
138
+ } from "@truefoundry/assistant-ui-runtime";
139
+
140
+ interface MySpec extends TfyAgentSpec {
141
+ workspaceId: string;
142
+ deploymentId: string;
143
+ }
144
+
145
+ const server: TrueFoundryChatServer<MySpec> = createTrueFoundryChatServer<MySpec>({
146
+ apiKey,
147
+ baseUrl,
148
+ });
149
+
150
+ const session = await server.getSession({ sessionId: "ses_abc" });
151
+ console.log(session.agentSpec?.workspaceId); // string | undefined
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Exports
157
+
158
+ | Export | Kind | Purpose |
159
+ | ------ | ---- | ------- |
160
+ | `createTrueFoundryChatServer` | Function | Build a `TrueFoundryChatServer` from gateway credentials / clients |
161
+ | `CreateTrueFoundryChatServerOptions` | Type | Options bag above |
162
+ | `TrueFoundryChatServer<TSpec>` | Type | `AgentChatServer` + `getGatewayClients()` |
163
+ | `TfyAgentSpec`, `TfySession`, `TfyTurn`, … | Types | Concrete gateway DTOs |
164
+ | `isTfyToolInfo`, `getTfyUsage`, … | Guards / helpers | Narrow / extract gateway event fields |
165
+
166
+ Import path:
167
+
168
+ ```ts
169
+ "@truefoundry/assistant-ui-runtime/plugins/truefoundry-agent-server-adapter"
170
+ ```
171
+
172
+ (or the main `@truefoundry/assistant-ui-runtime` entry, which re-exports these symbols).
173
+
174
+ ---
175
+
176
+ ## License
177
+
178
+ See [LICENSE](../../../../LICENSE).
@@ -0,0 +1,113 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ getTfyMcpInitServers,
4
+ getTfyThreadState,
5
+ getTfyUsage,
6
+ isTfyMcpToolInfo,
7
+ isTfySystemToolInfo,
8
+ isTfyToolInfo,
9
+ } from "./guards.js";
10
+
11
+ const usage = {
12
+ inputTokens: 10,
13
+ outputTokens: 5,
14
+ inputTokensBreakdown: {
15
+ harness: 1,
16
+ skills: 2,
17
+ instructions: 3,
18
+ toolDefinitions: 4,
19
+ messages: 5,
20
+ },
21
+ };
22
+
23
+ describe("tool info", () => {
24
+ it("discriminates on type", () => {
25
+ const system = { type: "truefoundry-system", name: "ask_user_question" };
26
+ const mcp = {
27
+ type: "mcp",
28
+ name: "search",
29
+ serverId: "s1",
30
+ serverName: "github",
31
+ };
32
+
33
+ expect(isTfySystemToolInfo(system)).toBe(true);
34
+ expect(isTfyMcpToolInfo(system)).toBe(false);
35
+ expect(isTfyMcpToolInfo(mcp)).toBe(true);
36
+ expect(isTfySystemToolInfo(mcp)).toBe(false);
37
+ expect(isTfyToolInfo(system) && isTfyToolInfo(mcp)).toBe(true);
38
+ });
39
+
40
+ it("rejects an mcp shape missing its server attribution", () => {
41
+ expect(isTfyMcpToolInfo({ type: "mcp", name: "search" })).toBe(false);
42
+ });
43
+
44
+ it("rejects absent and non-object values", () => {
45
+ for (const value of [undefined, null, "mcp", 0, []]) {
46
+ expect(isTfyToolInfo(value)).toBe(false);
47
+ }
48
+ });
49
+ });
50
+
51
+ describe("getTfyUsage", () => {
52
+ it("returns usage with its breakdown intact", () => {
53
+ expect(getTfyUsage({ usage })).toEqual(usage);
54
+ });
55
+
56
+ it("rejects usage whose breakdown is absent or incomplete", () => {
57
+ expect(getTfyUsage({ usage: { inputTokens: 1, outputTokens: 2 } })).toBeUndefined();
58
+ expect(
59
+ getTfyUsage({
60
+ usage: { ...usage, inputTokensBreakdown: { harness: 1 } },
61
+ }),
62
+ ).toBeUndefined();
63
+ });
64
+
65
+ it("returns undefined when there is no usage at all", () => {
66
+ expect(getTfyUsage({})).toBeUndefined();
67
+ expect(getTfyUsage(undefined)).toBeUndefined();
68
+ });
69
+ });
70
+
71
+ describe("getTfyThreadState", () => {
72
+ it("accepts done with output and error with a message", () => {
73
+ const done = { status: "done", output: { type: "model.message" } };
74
+ const errored = { status: "error", error: "boom" };
75
+
76
+ expect(getTfyThreadState({ state: done })).toEqual(done);
77
+ expect(getTfyThreadState({ state: errored })).toEqual(errored);
78
+ });
79
+
80
+ it("rejects done without output, since the gateway always sends one", () => {
81
+ expect(getTfyThreadState({ state: { status: "done" } })).toBeUndefined();
82
+ });
83
+
84
+ it("rejects an unknown status", () => {
85
+ expect(getTfyThreadState({ state: { status: "running" } })).toBeUndefined();
86
+ expect(getTfyThreadState({})).toBeUndefined();
87
+ });
88
+ });
89
+
90
+ describe("getTfyMcpInitServers", () => {
91
+ it("returns the servers when every entry is identifiable", () => {
92
+ const servers = [
93
+ { id: "a", name: "github", transportType: "http" },
94
+ { id: "b", name: "slack" },
95
+ ];
96
+ expect(getTfyMcpInitServers({ mcpServers: servers })).toEqual(servers);
97
+ });
98
+
99
+ it("rejects the whole array if any entry is malformed", () => {
100
+ expect(
101
+ getTfyMcpInitServers({ mcpServers: [{ id: "a", name: "ok" }, { id: "b" }] }),
102
+ ).toBeUndefined();
103
+ });
104
+
105
+ it("rejects a non-array", () => {
106
+ expect(getTfyMcpInitServers({ mcpServers: {} })).toBeUndefined();
107
+ expect(getTfyMcpInitServers({})).toBeUndefined();
108
+ });
109
+
110
+ it("accepts an empty list", () => {
111
+ expect(getTfyMcpInitServers({ mcpServers: [] })).toEqual([]);
112
+ });
113
+ });