@truefoundry/assistant-ui-runtime 0.1.6-rc.0 → 0.1.7

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 (38) hide show
  1. package/README.md +193 -579
  2. package/dist/chunk-3A2EPLQG.js +93 -0
  3. package/dist/chunk-3A2EPLQG.js.map +1 -0
  4. package/dist/chunk-SQDOTGP2.js +292 -0
  5. package/dist/chunk-SQDOTGP2.js.map +1 -0
  6. package/dist/index.d.ts +24 -36
  7. package/dist/index.js +276 -249
  8. package/dist/index.js.map +1 -1
  9. package/dist/plugins/truefoundry-agent-server-adapter/index.d.ts +134 -5
  10. package/dist/plugins/truefoundry-agent-server-adapter/index.js +16 -195
  11. package/dist/plugins/truefoundry-agent-server-adapter/index.js.map +1 -1
  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-VUBzoJT2.d.ts → types-DbNsU075.d.ts} +212 -19
  16. package/package.json +10 -5
  17. package/src/{private → draft}/agentSpec.ts +14 -17
  18. package/src/{private → draft}/draftSessionBridge.ts +1 -2
  19. package/src/{private → draft}/truefoundryDraftThreadListAdapter.test.ts +1 -1
  20. package/src/{private → draft}/truefoundryDraftThreadListAdapter.ts +2 -1
  21. package/src/{private → draft}/useDraftAgentSpec.ts +16 -5
  22. package/src/draftAgentConfig.test.ts +2 -1
  23. package/src/index.ts +71 -7
  24. package/src/plugins/truefoundry-agent-server-adapter/README.md +178 -0
  25. package/src/plugins/truefoundry-agent-server-adapter/guards.test.ts +113 -0
  26. package/src/plugins/truefoundry-agent-server-adapter/guards.ts +130 -0
  27. package/src/plugins/truefoundry-agent-server-adapter/index.ts +154 -40
  28. package/src/plugins/truefoundry-agent-server-adapter/types.ts +137 -0
  29. package/src/plugins/truefoundry-agent-server-adapter/types.typecheck.ts +164 -0
  30. package/src/server/index.ts +23 -0
  31. package/src/server/types.ts +272 -21
  32. package/src/truefoundryExtras.ts +4 -1
  33. package/src/truefoundryOwnedSessionsThreadListAdapter.ts +1 -1
  34. package/src/types.ts +1 -2
  35. package/src/useTrueFoundryAgentMessages.test.tsx +261 -1
  36. package/src/useTrueFoundryAgentMessages.ts +284 -176
  37. package/src/useTrueFoundryAgentRuntime.ts +31 -21
  38. /package/src/{private → draft}/useDraftAgentSpec.test.tsx +0 -0
@@ -4,18 +4,61 @@ import { PrivateAgentSessionClient } from "truefoundry-gateway-sdk/agents/privat
4
4
  import type { AgentDraftSession } from "truefoundry-gateway-sdk/agents/private";
5
5
  import type {
6
6
  AgentChatServer,
7
- AgentSpec,
8
7
  ListResult,
9
- Session,
10
- Turn,
11
8
  TurnInputItem,
12
9
  PreviousTurnIdInput,
10
+ UpdateSessionRequest,
13
11
  } from "../../server/types.js";
14
12
  import type {
13
+ SessionEventItem,
15
14
  TurnEvent,
16
15
  TurnStreamData,
17
- SessionEventItem,
18
16
  } from "../../server/events.js";
17
+ import type {
18
+ TfyAgentSpec,
19
+ TfyCreateSessionRequest,
20
+ TfyListSessionsParams,
21
+ TfySession,
22
+ TfyTurn,
23
+ TfyTurnState,
24
+ } from "./types.js";
25
+
26
+ export {
27
+ type TfyAgentSpec,
28
+ type TfySkillMount,
29
+ type TfyMcpServerMount,
30
+ type TfyModelParams,
31
+ type TfyRuntimeConfig,
32
+ type TfyResponseFormat,
33
+ type TfySubject,
34
+ type ToolsSelectorItem,
35
+ type ToolsSelectorTag,
36
+ type RequireApprovalToolSelectorItem,
37
+ type RequireApprovalToolsSelectorTag,
38
+ type TfyTurn,
39
+ type TfyTurnState,
40
+ type TfyTurnCancelledReason,
41
+ type TfyTurnStateDoneOutput,
42
+ type TfySession,
43
+ type TfyCreateSessionRequest,
44
+ type TfyListSessionsParams,
45
+ type TfyToolInfo,
46
+ type TfySystemToolInfo,
47
+ type TfyMcpToolInfo,
48
+ type TfyModelMessageUsage,
49
+ type TfyFinishReason,
50
+ type TfyThreadState,
51
+ type TfyMcpServerInitInfo,
52
+ } from "./types.js";
53
+
54
+ export {
55
+ isTfyToolInfo,
56
+ isTfySystemToolInfo,
57
+ isTfyMcpToolInfo,
58
+ getTfyUsage,
59
+ getTfyThreadState,
60
+ getTfyMcpInitServers,
61
+ } from "./guards.js";
19
62
 
20
63
  type GwSession = AgentSession | AgentDraftSession;
21
64
 
@@ -28,26 +71,51 @@ export type CreateTrueFoundryChatServerOptions = {
28
71
  deleteSession?: (req: { sessionId: string }) => Promise<void>;
29
72
  };
30
73
 
31
- export type TrueFoundryChatServer = AgentChatServer & {
32
- /** Escape hatch for hosts that still need raw gateway clients. */
33
- getGatewayClients(): {
34
- client: AgentSessionClient;
35
- privateClient: PrivateAgentSessionClient;
74
+ /**
75
+ * Only the spec is generic. Session/Turn/list-params are the concrete Tfy*
76
+ * types because the adapter builds them as fixed object literals — a generic
77
+ * there would type fields that nothing ever populates. The spec is safe: the
78
+ * gateway SDK serializes with `unrecognizedObjectKeys: "passthrough"`, so
79
+ * host-added spec fields survive the round trip.
80
+ */
81
+ export type TrueFoundryChatServer<TSpec extends TfyAgentSpec = TfyAgentSpec> =
82
+ AgentChatServer<
83
+ TSpec,
84
+ TfySession<TSpec>,
85
+ TfyCreateSessionRequest<TSpec>,
86
+ TfyListSessionsParams,
87
+ UpdateSessionRequest<TSpec>,
88
+ TfyTurn
89
+ > & {
90
+ /** Escape hatch for hosts that still need raw gateway clients. */
91
+ getGatewayClients(): {
92
+ client: AgentSessionClient;
93
+ privateClient: PrivateAgentSessionClient;
94
+ };
36
95
  };
37
- };
96
+
97
+ /** Gateway SDK errors carry the HTTP status as `statusCode`. */
98
+ function isNotFound(error: unknown): boolean {
99
+ return (
100
+ typeof error === "object" &&
101
+ error !== null &&
102
+ (error as { statusCode?: unknown }).statusCode === 404
103
+ );
104
+ }
38
105
 
39
106
  function isDraft(session: GwSession): session is AgentDraftSession {
40
107
  return (session as AgentDraftSession).type === "session/draft";
41
108
  }
42
109
 
43
- function toSession(raw: GwSession): Session {
110
+ function toSession<TSpec extends TfyAgentSpec>(raw: GwSession): TfySession<TSpec> {
44
111
  const mutable = isDraft(raw);
45
112
  return {
46
113
  id: raw.id,
47
114
  title: raw.title,
48
115
  agentName: raw.agentName,
49
- ...(mutable ? { agentSpec: raw.agentSpec as AgentSpec } : {}),
116
+ ...(mutable ? { agentSpec: raw.agentSpec as TSpec } : {}),
50
117
  isMutable: mutable,
118
+ createdBySubject: raw.createdBySubject,
51
119
  createdAt: raw.createdAt,
52
120
  updatedAt: raw.updatedAt,
53
121
  };
@@ -58,15 +126,17 @@ function toTurn(raw: {
58
126
  sessionId: string;
59
127
  previousTurnId?: string | null;
60
128
  input?: TurnInputItem[];
61
- state: Turn["state"];
129
+ state: unknown;
130
+ createdBySubject: TfyTurn["createdBySubject"];
62
131
  createdAt: string;
63
- }): Turn {
132
+ }): TfyTurn {
64
133
  return {
65
134
  id: raw.id,
66
135
  sessionId: raw.sessionId,
67
136
  previousTurnId: raw.previousTurnId,
68
- input: raw.input as TurnInputItem[] | undefined,
69
- state: raw.state as Turn["state"],
137
+ input: raw.input,
138
+ state: raw.state as TfyTurnState,
139
+ createdBySubject: raw.createdBySubject,
70
140
  createdAt: raw.createdAt,
71
141
  };
72
142
  }
@@ -92,52 +162,94 @@ async function toListResult<TIn, TOut>(
92
162
  * Wraps TrueFoundry gateway clients into a flat `AgentChatServer`.
93
163
  * Named vs draft routing is fully internal — an in-memory session-type cache
94
164
  * (populated by createSession/listSessions) determines which gateway client
95
- * to call. No try/catch fallback, no double network calls.
165
+ * to call, falling back to a one-time probe for ids seen only in a URL.
96
166
  */
97
- export function createTrueFoundryChatServer(
167
+ export function createTrueFoundryChatServer<
168
+ TSpec extends TfyAgentSpec = TfyAgentSpec,
169
+ >(
98
170
  opts: CreateTrueFoundryChatServerOptions,
99
- ): TrueFoundryChatServer {
171
+ ): TrueFoundryChatServer<TSpec> {
100
172
  const gatewayOpts = { apiKey: opts.apiKey, baseUrl: opts.baseUrl };
101
173
  const client = opts.client ?? new AgentSessionClient(gatewayOpts);
102
174
  const privateClient =
103
175
  opts.privateClient ?? new PrivateAgentSessionClient(gatewayOpts);
104
176
 
105
177
  const sessionTypeCache = new Map<string, boolean>();
178
+ const sessionTypeProbes = new Map<string, Promise<boolean>>();
106
179
 
107
- function cacheSessionType(session: Session): void {
180
+ function cacheSessionType(session: {
181
+ id: string;
182
+ isMutable: boolean;
183
+ }): void {
108
184
  sessionTypeCache.set(session.id, session.isMutable);
109
185
  }
110
186
 
111
- function getSessionObj(sessionId: string): Promise<GwSession> {
112
- const isMutable = sessionTypeCache.get(sessionId);
113
- if (isMutable === true) {
114
- return privateClient.getDraftSession({ draftSessionId: sessionId });
187
+ /**
188
+ * Resolving a session id straight from a URL (page reload / shared link)
189
+ * reaches the gateway before createSession or listSessions has cached the
190
+ * type, so discover it by probing the draft endpoint and falling back to
191
+ * the conversation endpoint. Concurrent callers share one probe.
192
+ */
193
+ async function probeSessionType(sessionId: string): Promise<boolean> {
194
+ const inflight = sessionTypeProbes.get(sessionId);
195
+ if (inflight != null) {
196
+ return inflight;
115
197
  }
116
- if (isMutable === false) {
117
- return client.getSession({ sessionId });
198
+ const probe = (async () => {
199
+ try {
200
+ await privateClient.getDraftSession({ draftSessionId: sessionId });
201
+ return true;
202
+ } catch (error) {
203
+ // Only a miss proves the id isn't a draft. Auth or transient
204
+ // failures would otherwise be retried as a conversation session
205
+ // and mislabel a real draft as named.
206
+ if (!isNotFound(error)) {
207
+ throw error;
208
+ }
209
+ await client.getSession({ sessionId });
210
+ return false;
211
+ }
212
+ })();
213
+ sessionTypeProbes.set(sessionId, probe);
214
+ try {
215
+ const isMutable = await probe;
216
+ sessionTypeCache.set(sessionId, isMutable);
217
+ return isMutable;
218
+ } finally {
219
+ sessionTypeProbes.delete(sessionId);
118
220
  }
119
- throw new Error(
120
- `Cannot resolve session "${sessionId}": session type not cached. ` +
121
- `Ensure createSession or listSessions was called first.`,
122
- );
123
221
  }
124
222
 
125
- const server: TrueFoundryChatServer = {
223
+ async function getSessionObj(sessionId: string): Promise<GwSession> {
224
+ const isMutable =
225
+ sessionTypeCache.get(sessionId) ?? (await probeSessionType(sessionId));
226
+ return isMutable
227
+ ? privateClient.getDraftSession({ draftSessionId: sessionId })
228
+ : client.getSession({ sessionId });
229
+ }
230
+
231
+ const server: TrueFoundryChatServer<TSpec> = {
126
232
  async createSession(req) {
127
233
  if (req.agentSpec != null) {
128
234
  const draft = await privateClient.createDraftSession({
129
- agentSpec: req.agentSpec as never,
235
+ agentSpec: req.agentSpec,
130
236
  ...(req.agentName != null ? { agentName: req.agentName } : {}),
237
+ ...(req.tfyMetadata != null
238
+ ? { tfyMetadata: req.tfyMetadata }
239
+ : {}),
131
240
  });
132
- const session = toSession(draft);
241
+ const session = toSession<TSpec>(draft);
133
242
  cacheSessionType(session);
134
243
  return session;
135
244
  }
136
245
  if (req.agentName != null) {
137
246
  const named = await client.createSession({
138
247
  agentName: req.agentName,
248
+ ...(req.tfyMetadata != null
249
+ ? { tfyMetadata: req.tfyMetadata }
250
+ : {}),
139
251
  });
140
- const session = toSession(named);
252
+ const session = toSession<TSpec>(named);
141
253
  cacheSessionType(session);
142
254
  return session;
143
255
  }
@@ -150,9 +262,10 @@ export function createTrueFoundryChatServer(
150
262
  order: req?.order,
151
263
  pageToken: req?.pageToken,
152
264
  startTimestamp: req?.startTimestamp,
265
+ endTimestamp: req?.endTimestamp,
153
266
  ...(req?.agentName != null ? { agentName: req.agentName } : {}),
154
267
  });
155
- const result = await toListResult(page, toSession);
268
+ const result = await toListResult(page, (s) => toSession<TSpec>(s));
156
269
  for (const session of result.data) {
157
270
  cacheSessionType(session);
158
271
  }
@@ -161,7 +274,7 @@ export function createTrueFoundryChatServer(
161
274
 
162
275
  async getSession({ sessionId }) {
163
276
  const raw = await getSessionObj(sessionId);
164
- const session = toSession(raw);
277
+ const session = toSession<TSpec>(raw);
165
278
  cacheSessionType(session);
166
279
  return session;
167
280
  },
@@ -174,9 +287,9 @@ export function createTrueFoundryChatServer(
174
287
  );
175
288
  }
176
289
  if (req.agentSpec != null) {
177
- await raw.update({ agentSpec: req.agentSpec as never });
290
+ await raw.update({ agentSpec: req.agentSpec });
178
291
  }
179
- return toSession(raw);
292
+ return toSession<TSpec>(raw);
180
293
  },
181
294
 
182
295
  prepareAndExecuteTurn(req: {
@@ -217,12 +330,13 @@ export function createTrueFoundryChatServer(
217
330
  await opts.deleteSession({ sessionId });
218
331
  },
219
332
 
220
- async listTurns({ sessionId, limit, pageToken, order }) {
333
+ // The runtime's signature offers `order`, but the gateway's listTurns
334
+ // takes no such param — forwarding it silently did nothing.
335
+ async listTurns({ sessionId, limit, pageToken }) {
221
336
  const raw = await getSessionObj(sessionId);
222
337
  const page = await raw.listTurns({
223
338
  ...(limit != null ? { limit } : {}),
224
339
  ...(pageToken != null ? { pageToken } : {}),
225
- ...(order != null ? { order } : {}),
226
340
  });
227
341
  return toListResult(page, (turn) => toTurn(turn));
228
342
  },
@@ -0,0 +1,137 @@
1
+ /**
2
+ * TrueFoundry-specific type extensions over the runtime's generic bases.
3
+ *
4
+ * The runtime defines minimal bases (SkillMount = {id,name}, McpServerMount =
5
+ * {id,name}, AgentSpec.config = unknown, etc.) that hosts extend via generics.
6
+ *
7
+ * This file builds the concrete TrueFoundry types by:
8
+ * - Importing the runtime's AgentSpec as the base to extend.
9
+ * - Importing concrete sub-types from the gateway SDK namespace rather than
10
+ * re-defining them — they're purely gateway concepts.
11
+ * - Composing a TfyAgentSpec that satisfies both the runtime's
12
+ * `TSpec extends AgentSpec` constraint and the gateway SDK's AgentSpec shape.
13
+ */
14
+
15
+ import type {
16
+ AgentSpec,
17
+ CreateSessionRequest,
18
+ ListSessionsParams,
19
+ Session,
20
+ Turn,
21
+ TurnState,
22
+ } from "../../server/types.js";
23
+ import type { TruefoundryGatewayApi } from "truefoundry-gateway-sdk";
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Re-exports — gateway sub-types surfaced for host convenience
27
+ // ---------------------------------------------------------------------------
28
+
29
+ export type TfyRuntimeConfig = TruefoundryGatewayApi.RuntimeConfig;
30
+ export type TfyResponseFormat = TruefoundryGatewayApi.ResponseFormat;
31
+ export type TfyModelParams = TruefoundryGatewayApi.ModelParams;
32
+ export type TfySubject = TruefoundryGatewayApi.Subject;
33
+
34
+ // ---------------------------------------------------------------------------
35
+ // Tool selector helpers (mirrors gateway SDK enum values)
36
+ // ---------------------------------------------------------------------------
37
+
38
+ export type ToolsSelectorTag = "@all" | "@read-only";
39
+ export type RequireApprovalToolsSelectorTag = "@all" | "@write" | "@destructive";
40
+ export type ToolsSelectorItem = ToolsSelectorTag | string;
41
+ export type RequireApprovalToolSelectorItem = RequireApprovalToolsSelectorTag | string;
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Runtime mount bases — derived from AgentSpec, which the runtime does export
45
+ // (SkillMount / McpServerMount themselves are not part of its public API).
46
+ // ---------------------------------------------------------------------------
47
+
48
+ type RuntimeSkillMount = NonNullable<NonNullable<AgentSpec["skills"]>[number]>;
49
+ type RuntimeMcpServerMount = NonNullable<NonNullable<AgentSpec["mcpServers"]>[number]>;
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Mounts — runtime base + gateway fields (fqn, type, url, tool selectors)
53
+ // ---------------------------------------------------------------------------
54
+
55
+ // Both gateway types are unions (git vs registry source, inline vs registry
56
+ // server), so these must be intersections — an interface cannot `extends` a
57
+ // union, and doing so silently degrades to the runtime base under skipLibCheck.
58
+ export type TfySkillMount = RuntimeSkillMount & TruefoundryGatewayApi.SkillMount;
59
+
60
+ export type TfyMcpServerMount = RuntimeMcpServerMount & TruefoundryGatewayApi.McpServer;
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // AgentSpec — the concrete TrueFoundry agent definition
64
+ // ---------------------------------------------------------------------------
65
+
66
+ export interface TfyAgentSpec
67
+ extends AgentSpec<
68
+ TruefoundryGatewayApi.Model,
69
+ TfySkillMount,
70
+ TfyMcpServerMount
71
+ > {
72
+ config?: TruefoundryGatewayApi.RuntimeConfig;
73
+ responseFormat?: TruefoundryGatewayApi.ResponseFormat;
74
+ messages?: TruefoundryGatewayApi.AgentSpecUserMessage[];
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // Turn — runtime base narrowed to the gateway's concrete state shapes
79
+ // ---------------------------------------------------------------------------
80
+
81
+ export type TfyTurnCancelledReason = TruefoundryGatewayApi.TurnStateCancelledReason;
82
+ export type TfyTurnStateDoneOutput = TruefoundryGatewayApi.TurnStateDoneOutput;
83
+
84
+ type RuntimeTurnStateDone = Extract<TurnState, { status: "done" }>;
85
+ type RuntimeTurnStateCancelled = Extract<TurnState, { status: "cancelled" }>;
86
+
87
+ /**
88
+ * The runtime types `output` as `unknown` and `reason` as bare `string`. The
89
+ * gateway sends a model message and one of four reasons — `cancelled-for-next-turn`
90
+ * in particular is routine and should not render like a failure.
91
+ */
92
+ export type TfyTurnState =
93
+ | Exclude<TurnState, { status: "done" | "cancelled" }>
94
+ | (Omit<RuntimeTurnStateDone, "output"> & { output?: TfyTurnStateDoneOutput })
95
+ | (Omit<RuntimeTurnStateCancelled, "reason"> & { reason: TfyTurnCancelledReason });
96
+
97
+ export interface TfyTurn extends Turn {
98
+ state: TfyTurnState;
99
+ createdBySubject: TfySubject;
100
+ }
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // Session and request params — runtime bases + gateway-only fields
104
+ // ---------------------------------------------------------------------------
105
+
106
+ export interface TfySession<TSpec extends TfyAgentSpec = TfyAgentSpec>
107
+ extends Session<TSpec> {
108
+ createdBySubject: TfySubject;
109
+ }
110
+
111
+ export interface TfyCreateSessionRequest<TSpec extends TfyAgentSpec = TfyAgentSpec>
112
+ extends CreateSessionRequest<TSpec> {
113
+ /** Sent as `x-tfy-metadata`, persisted server-side as `request_metadata`. */
114
+ tfyMetadata?: string;
115
+ }
116
+
117
+ export interface TfyListSessionsParams extends ListSessionsParams {
118
+ /** Inclusive upper bound on `createdAt` (ISO-8601). */
119
+ endTimestamp?: string;
120
+ }
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // Event-side types.
124
+ //
125
+ // These do NOT flow through AgentChatServer — its listEvents / listTurnEvents /
126
+ // subscribeToTurn / prepareAndExecuteTurn signatures hardcode the runtime's
127
+ // event types with no generic to override. Hosts narrow at the point of use
128
+ // with the guards in `guards.ts`.
129
+ // ---------------------------------------------------------------------------
130
+
131
+ export type TfyToolInfo = TruefoundryGatewayApi.ToolInfo;
132
+ export type TfySystemToolInfo = TruefoundryGatewayApi.TrueFoundrySystemToolInfo;
133
+ export type TfyMcpToolInfo = TruefoundryGatewayApi.McpToolInfo;
134
+ export type TfyModelMessageUsage = TruefoundryGatewayApi.ModelMessageUsage;
135
+ export type TfyFinishReason = TruefoundryGatewayApi.FinishReason;
136
+ export type TfyThreadState = TruefoundryGatewayApi.ThreadState;
137
+ export type TfyMcpServerInitInfo = TruefoundryGatewayApi.McpServerInitInfo;
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Compile-time assertions for the mount types in `types.ts`.
3
+ *
4
+ * TfySkillMount / TfyMcpServerMount are built by indexing into the runtime's
5
+ * exported AgentSpec. If that indexed access ever degrades to `unknown`, the
6
+ * intersection becomes a silent no-op and the gateway fields disappear without
7
+ * any error. These assertions fail `pnpm typecheck` if that happens.
8
+ *
9
+ * Not an entry point — tsup only bundles src/index.ts.
10
+ */
11
+
12
+ import type { AgentSpec } from "../../server/types.js";
13
+ import type { createTrueFoundryChatServer } from "./index.js";
14
+ import type {
15
+ TfyAgentSpec,
16
+ TfyFinishReason,
17
+ TfyMcpServerMount,
18
+ TfySkillMount,
19
+ TfySubject,
20
+ TfyTurn,
21
+ } from "./types.js";
22
+
23
+ // Both gateway source variants must survive the intersection with the base.
24
+ export const registrySkill: TfySkillMount = {
25
+ type: "truefoundry-skills-registry",
26
+ fqn: "tfy:skill:v1",
27
+ preload: true,
28
+ };
29
+
30
+ export const gitSkill: TfySkillMount = {
31
+ type: "git",
32
+ url: "https://github.com/acme/skills",
33
+ name: "reviewer",
34
+ ref: "main",
35
+ };
36
+
37
+ // @ts-expect-error the type discriminant must survive the intersection
38
+ export const skillWithoutType: TfySkillMount = { fqn: "a" };
39
+
40
+ // @ts-expect-error registry variant requires fqn
41
+ export const registrySkillWithoutFqn: TfySkillMount = {
42
+ type: "truefoundry-skills-registry",
43
+ };
44
+
45
+ export const registeredMcp: TfyMcpServerMount = {
46
+ name: "github",
47
+ type: "truefoundry-mcp-registry",
48
+ enableTools: ["@read-only"],
49
+ requireApprovalForTools: ["@destructive"],
50
+ };
51
+
52
+ export const inlineMcp: TfyMcpServerMount = {
53
+ name: "custom",
54
+ type: "inline",
55
+ url: "https://example.com/mcp",
56
+ };
57
+
58
+ // @ts-expect-error the type discriminant must survive the intersection
59
+ export const mcpWithoutType: TfyMcpServerMount = { name: "a" };
60
+
61
+ // @ts-expect-error inline variant requires url
62
+ export const inlineMcpWithoutUrl: TfyMcpServerMount = {
63
+ name: "a",
64
+ type: "inline",
65
+ };
66
+
67
+ // The gateway's own spec — a registry skill carries neither `id` nor `name` —
68
+ // must satisfy the runtime base. A base that rejects it makes every server
69
+ // response unassignable, which is the regression these mounts are guarding.
70
+ declare const gatewayShapedSpec: TfyAgentSpec;
71
+ export const asRuntimeSpec: AgentSpec = gatewayShapedSpec;
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // Generic TSpec — a host extension must reach the returned server's types,
75
+ // and the bare (defaulted) form must keep working.
76
+ // ---------------------------------------------------------------------------
77
+
78
+ interface HostSpec extends TfyAgentSpec {
79
+ workspaceId: string;
80
+ }
81
+
82
+ type HostServer = ReturnType<typeof createTrueFoundryChatServer<HostSpec>>;
83
+ type DefaultServer = ReturnType<typeof createTrueFoundryChatServer>;
84
+
85
+ declare const hostSession: Awaited<ReturnType<HostServer["getSession"]>>;
86
+ declare const defaultSession: Awaited<ReturnType<DefaultServer["getSession"]>>;
87
+
88
+ // Host field is visible on the spec returned by the generic server.
89
+ export const workspaceId: string | undefined = hostSession.agentSpec?.workspaceId;
90
+
91
+ // Gateway fields still flow through alongside it.
92
+ export const hostSkills: TfySkillMount[] | undefined = hostSession.agentSpec?.skills;
93
+
94
+ // @ts-expect-error the default instantiation must NOT have the host's field
95
+ export const leakedWorkspaceId = defaultSession.agentSpec?.workspaceId;
96
+
97
+ // Host spec is accepted where the server expects one.
98
+ export declare function createHostSession(
99
+ server: HostServer,
100
+ spec: HostSpec,
101
+ ): ReturnType<HostServer["createSession"]>;
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // Turn — the gateway narrowings must not collapse back into the runtime's
105
+ // `unknown` output / bare-`string` reason.
106
+ // ---------------------------------------------------------------------------
107
+
108
+ const subject: TfySubject = {
109
+ subjectId: "u-1",
110
+ subjectType: "user",
111
+ subjectSlug: "someone",
112
+ };
113
+
114
+ export const cancelledTurn: TfyTurn = {
115
+ id: "turn-1",
116
+ sessionId: "session-1",
117
+ state: {
118
+ status: "cancelled",
119
+ reason: "cancelled-for-next-turn",
120
+ completedAt: "2026-01-01T00:00:00Z",
121
+ },
122
+ createdBySubject: subject,
123
+ createdAt: "2026-01-01T00:00:00Z",
124
+ };
125
+
126
+ export const freeformCancelReason: TfyTurn = {
127
+ ...cancelledTurn,
128
+ // @ts-expect-error reason is one of four gateway values, not any string
129
+ state: { status: "cancelled", reason: "nope", completedAt: "2026-01-01T00:00:00Z" },
130
+ };
131
+
132
+ // @ts-expect-error createdBySubject is required — the gateway always sends it
133
+ export const turnWithoutSubject: TfyTurn = {
134
+ id: "turn-1",
135
+ sessionId: "session-1",
136
+ state: { status: "running" },
137
+ createdAt: "2026-01-01T00:00:00Z",
138
+ };
139
+
140
+ declare const doneTurn: Extract<TfyTurn["state"], { status: "done" }>;
141
+
142
+ // `output` is the gateway's model message; on the runtime base this is `unknown`
143
+ // and would not permit a property access at all.
144
+ export const doneFinishReason: TfyFinishReason | null | undefined =
145
+ doneTurn.output?.finishReason;
146
+ export const doneTokens: number | undefined = doneTurn.output?.usage?.inputTokens;
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // Session / request params — gateway-only fields reach the server methods.
150
+ // ---------------------------------------------------------------------------
151
+
152
+ export const sessionSubject: TfySubject = hostSession.createdBySubject;
153
+
154
+ export const withMetadata: Parameters<DefaultServer["createSession"]>[0] = {
155
+ agentName: "agent",
156
+ tfyMetadata: JSON.stringify({ tenant: "acme" }),
157
+ };
158
+
159
+ export const withEndTimestamp: NonNullable<
160
+ Parameters<DefaultServer["listSessions"]>[0]
161
+ > = {
162
+ startTimestamp: "2026-01-01T00:00:00Z",
163
+ endTimestamp: "2026-02-01T00:00:00Z",
164
+ };
@@ -31,6 +31,29 @@ export type {
31
31
  Turn,
32
32
  AgentChatServer,
33
33
  AgentBuilderServer,
34
+ ProviderType,
35
+ ModelEntry,
36
+ ModelProviderConfigBase,
37
+ ModelProviderBase,
38
+ ModelProviderCatalogEntry,
39
+ CreateModelProviderRequest,
40
+ UpdateModelProviderRequest,
41
+ ModelCatalogServer,
42
+ ToolBase,
43
+ ConnectorAuthType,
44
+ ConnectorAuth,
45
+ ConnectorAuthPublic,
46
+ ConnectorConfigBase,
47
+ ConnectorBase,
48
+ ConnectorCatalogEntry,
49
+ CreateConnectorRequest,
50
+ UpdateConnectorRequest,
51
+ ConnectorCatalogServer,
52
+ SkillBase,
53
+ CreateSkillRequest,
54
+ SkillCatalogServer,
55
+ CatalogServer,
56
+ AgentUIServerPort,
34
57
  } from "./types.js";
35
58
 
36
59
  export type {