@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.
- package/README.md +193 -579
- package/dist/chunk-3A2EPLQG.js +93 -0
- package/dist/chunk-3A2EPLQG.js.map +1 -0
- package/dist/chunk-SQDOTGP2.js +292 -0
- package/dist/chunk-SQDOTGP2.js.map +1 -0
- package/dist/index.d.ts +24 -36
- package/dist/index.js +276 -249
- package/dist/index.js.map +1 -1
- package/dist/plugins/truefoundry-agent-server-adapter/index.d.ts +134 -5
- package/dist/plugins/truefoundry-agent-server-adapter/index.js +16 -195
- package/dist/plugins/truefoundry-agent-server-adapter/index.js.map +1 -1
- package/dist/server/index.d.ts +17 -0
- package/dist/server/index.js +9 -0
- package/dist/server/index.js.map +1 -0
- package/dist/{types-VUBzoJT2.d.ts → types-DbNsU075.d.ts} +212 -19
- package/package.json +10 -5
- package/src/{private → draft}/agentSpec.ts +14 -17
- package/src/{private → draft}/draftSessionBridge.ts +1 -2
- package/src/{private → draft}/truefoundryDraftThreadListAdapter.test.ts +1 -1
- package/src/{private → draft}/truefoundryDraftThreadListAdapter.ts +2 -1
- package/src/{private → draft}/useDraftAgentSpec.ts +16 -5
- package/src/draftAgentConfig.test.ts +2 -1
- package/src/index.ts +71 -7
- package/src/plugins/truefoundry-agent-server-adapter/README.md +178 -0
- package/src/plugins/truefoundry-agent-server-adapter/guards.test.ts +113 -0
- package/src/plugins/truefoundry-agent-server-adapter/guards.ts +130 -0
- package/src/plugins/truefoundry-agent-server-adapter/index.ts +154 -40
- package/src/plugins/truefoundry-agent-server-adapter/types.ts +137 -0
- package/src/plugins/truefoundry-agent-server-adapter/types.typecheck.ts +164 -0
- package/src/server/index.ts +23 -0
- package/src/server/types.ts +272 -21
- package/src/truefoundryExtras.ts +4 -1
- package/src/truefoundryOwnedSessionsThreadListAdapter.ts +1 -1
- package/src/types.ts +1 -2
- package/src/useTrueFoundryAgentMessages.test.tsx +261 -1
- package/src/useTrueFoundryAgentMessages.ts +284 -176
- package/src/useTrueFoundryAgentRuntime.ts +31 -21
- /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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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):
|
|
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
|
|
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:
|
|
129
|
+
state: unknown;
|
|
130
|
+
createdBySubject: TfyTurn["createdBySubject"];
|
|
62
131
|
createdAt: string;
|
|
63
|
-
}):
|
|
132
|
+
}): TfyTurn {
|
|
64
133
|
return {
|
|
65
134
|
id: raw.id,
|
|
66
135
|
sessionId: raw.sessionId,
|
|
67
136
|
previousTurnId: raw.previousTurnId,
|
|
68
|
-
input: raw.input
|
|
69
|
-
state: raw.state as
|
|
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
|
|
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:
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
117
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
+
};
|
package/src/server/index.ts
CHANGED
|
@@ -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 {
|