@truefoundry/assistant-ui-runtime 0.1.7 → 0.1.8
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/CHANGELOG.md +23 -0
- package/README.md +19 -15
- package/dist/chunk-CXBZ6WLZ.js +636 -0
- package/dist/chunk-CXBZ6WLZ.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +14 -5
- package/dist/index.js.map +1 -1
- package/dist/plugins/truefoundry-agent-server-adapter/index.d.ts +81 -35
- package/dist/plugins/truefoundry-agent-server-adapter/index.js +3 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/{types-DbNsU075.d.ts → types-B_z-FsDS.d.ts} +32 -21
- package/package.json +1 -1
- package/src/convertTurnMessages.ts +4 -0
- package/src/draft/truefoundryDraftThreadListAdapter.ts +4 -1
- package/src/harness.temp.ts +85 -0
- package/src/index.ts +13 -0
- package/src/plugins/truefoundry-agent-server-adapter/README.md +83 -44
- package/src/plugins/truefoundry-agent-server-adapter/chatServer.ts +365 -0
- package/src/plugins/truefoundry-agent-server-adapter/cp.test.ts +444 -0
- package/src/plugins/truefoundry-agent-server-adapter/cp.ts +482 -0
- package/src/plugins/truefoundry-agent-server-adapter/createTrueFoundryAgentUIServer.ts +94 -0
- package/src/plugins/truefoundry-agent-server-adapter/guards.ts +1 -1
- package/src/plugins/truefoundry-agent-server-adapter/index.ts +20 -391
- package/src/plugins/truefoundry-agent-server-adapter/normalizeAgentSpec.test.ts +85 -0
- package/src/plugins/truefoundry-agent-server-adapter/normalizeAgentSpec.ts +84 -0
- package/src/plugins/truefoundry-agent-server-adapter/types.ts +1 -1
- package/src/server/index.ts +6 -0
- package/src/server/types.ts +30 -23
- package/src/streamTurn.test.ts +27 -27
- package/src/streamTurn.ts +2 -2
- package/src/truefoundryOwnedSessionsThreadListAdapter.ts +4 -1
- package/src/truefoundryThreadListAdapter.test.ts +22 -0
- package/src/truefoundryThreadListAdapter.ts +4 -1
- package/src/useTrueFoundryAgentMessages.test.tsx +1 -1
- package/dist/chunk-SQDOTGP2.js +0 -292
- package/dist/chunk-SQDOTGP2.js.map +0 -1
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
// src/plugins/truefoundry-agent-server-adapter/chatServer.ts
|
|
2
|
+
import { AgentSessionClient } from "truefoundry-gateway-sdk/agents";
|
|
3
|
+
import { PrivateAgentSessionClient } from "truefoundry-gateway-sdk/agents/private";
|
|
4
|
+
|
|
5
|
+
// src/plugins/truefoundry-agent-server-adapter/normalizeAgentSpec.ts
|
|
6
|
+
function isRecord(value) {
|
|
7
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
function normalizeMcpMount(raw) {
|
|
10
|
+
if (!isRecord(raw)) {
|
|
11
|
+
throw new Error("mcpServers entry must be an object");
|
|
12
|
+
}
|
|
13
|
+
if (raw.type === "truefoundry-mcp-registry" || raw.type === "inline") {
|
|
14
|
+
return raw;
|
|
15
|
+
}
|
|
16
|
+
const name = (typeof raw.name === "string" && raw.name !== "" ? raw.name : null) ?? (typeof raw.mcpName === "string" && raw.mcpName !== "" ? raw.mcpName : null) ?? (typeof raw.id === "string" && raw.id !== "" ? raw.id : null);
|
|
17
|
+
if (name == null) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
"mcpServers entry needs name, mcpName, or id to mount as registry MCP"
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
return { type: "truefoundry-mcp-registry", name };
|
|
23
|
+
}
|
|
24
|
+
function normalizeSkillMount(raw) {
|
|
25
|
+
if (!isRecord(raw)) {
|
|
26
|
+
throw new Error("skills entry must be an object");
|
|
27
|
+
}
|
|
28
|
+
if (raw.type === "truefoundry-skills-registry" || raw.type === "git") {
|
|
29
|
+
return raw;
|
|
30
|
+
}
|
|
31
|
+
const fqn = (typeof raw.fqn === "string" && raw.fqn !== "" ? raw.fqn : null) ?? (typeof raw.id === "string" && raw.id !== "" ? raw.id : null);
|
|
32
|
+
if (fqn == null) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
"skills entry needs fqn or id to mount as registry skill"
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
type: "truefoundry-skills-registry",
|
|
39
|
+
fqn,
|
|
40
|
+
...raw.preload === true ? { preload: true } : {}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function normalizeAgentSpecForGateway(spec) {
|
|
44
|
+
return {
|
|
45
|
+
...spec,
|
|
46
|
+
...spec.mcpServers != null ? {
|
|
47
|
+
mcpServers: spec.mcpServers.map(
|
|
48
|
+
normalizeMcpMount
|
|
49
|
+
)
|
|
50
|
+
} : {},
|
|
51
|
+
...spec.skills != null ? {
|
|
52
|
+
skills: spec.skills.map(
|
|
53
|
+
normalizeSkillMount
|
|
54
|
+
)
|
|
55
|
+
} : {}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/plugins/truefoundry-agent-server-adapter/chatServer.ts
|
|
60
|
+
function isNotFound(error) {
|
|
61
|
+
return typeof error === "object" && error !== null && error.statusCode === 404;
|
|
62
|
+
}
|
|
63
|
+
function isDraft(session) {
|
|
64
|
+
return session.type === "session/draft";
|
|
65
|
+
}
|
|
66
|
+
function toSession(raw) {
|
|
67
|
+
const mutable = isDraft(raw);
|
|
68
|
+
return {
|
|
69
|
+
id: raw.id,
|
|
70
|
+
title: raw.title,
|
|
71
|
+
agentName: raw.agentName,
|
|
72
|
+
...mutable ? { agentSpec: raw.agentSpec } : {},
|
|
73
|
+
isMutable: mutable,
|
|
74
|
+
createdBySubject: raw.createdBySubject,
|
|
75
|
+
createdAt: raw.createdAt,
|
|
76
|
+
updatedAt: raw.updatedAt
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function toTurn(raw) {
|
|
80
|
+
return {
|
|
81
|
+
id: raw.id,
|
|
82
|
+
sessionId: raw.sessionId,
|
|
83
|
+
previousTurnId: raw.previousTurnId,
|
|
84
|
+
input: raw.input,
|
|
85
|
+
state: raw.state,
|
|
86
|
+
createdBySubject: raw.createdBySubject,
|
|
87
|
+
createdAt: raw.createdAt
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async function toListResult(page, map) {
|
|
91
|
+
const nextPageToken = page.response?.pagination?.nextPageToken;
|
|
92
|
+
return {
|
|
93
|
+
data: page.data.map(map),
|
|
94
|
+
...nextPageToken != null && nextPageToken !== "" ? { nextPageToken } : {}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function createTrueFoundryChatServer(opts) {
|
|
98
|
+
const gatewayOpts = { apiKey: opts.apiKey, baseUrl: opts.baseUrl };
|
|
99
|
+
const client = opts.client ?? new AgentSessionClient(gatewayOpts);
|
|
100
|
+
const privateClient = opts.privateClient ?? new PrivateAgentSessionClient(gatewayOpts);
|
|
101
|
+
const sessionTypeCache = /* @__PURE__ */ new Map();
|
|
102
|
+
const sessionTypeProbes = /* @__PURE__ */ new Map();
|
|
103
|
+
function cacheSessionType(session) {
|
|
104
|
+
sessionTypeCache.set(session.id, session.isMutable);
|
|
105
|
+
}
|
|
106
|
+
async function probeSessionType(sessionId) {
|
|
107
|
+
const inflight2 = sessionTypeProbes.get(sessionId);
|
|
108
|
+
if (inflight2 != null) {
|
|
109
|
+
return inflight2;
|
|
110
|
+
}
|
|
111
|
+
const probe = (async () => {
|
|
112
|
+
try {
|
|
113
|
+
await privateClient.getDraftSession({ draftSessionId: sessionId });
|
|
114
|
+
return true;
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (!isNotFound(error)) {
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
await client.getSession({ sessionId });
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
})();
|
|
123
|
+
sessionTypeProbes.set(sessionId, probe);
|
|
124
|
+
try {
|
|
125
|
+
const isMutable = await probe;
|
|
126
|
+
sessionTypeCache.set(sessionId, isMutable);
|
|
127
|
+
return isMutable;
|
|
128
|
+
} finally {
|
|
129
|
+
sessionTypeProbes.delete(sessionId);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function getSessionObj(sessionId) {
|
|
133
|
+
const isMutable = sessionTypeCache.get(sessionId) ?? await probeSessionType(sessionId);
|
|
134
|
+
return isMutable ? privateClient.getDraftSession({ draftSessionId: sessionId }) : client.getSession({ sessionId });
|
|
135
|
+
}
|
|
136
|
+
const server = {
|
|
137
|
+
async createSession(req) {
|
|
138
|
+
if (req.agentSpec != null) {
|
|
139
|
+
const draft = await privateClient.createDraftSession({
|
|
140
|
+
agentSpec: normalizeAgentSpecForGateway(req.agentSpec),
|
|
141
|
+
...req.agentName != null ? { agentName: req.agentName } : {},
|
|
142
|
+
...req.tfyMetadata != null ? { tfyMetadata: req.tfyMetadata } : {}
|
|
143
|
+
});
|
|
144
|
+
const session = toSession(draft);
|
|
145
|
+
cacheSessionType(session);
|
|
146
|
+
return session;
|
|
147
|
+
}
|
|
148
|
+
if (req.agentName != null) {
|
|
149
|
+
const named = await client.createSession({
|
|
150
|
+
agentName: req.agentName,
|
|
151
|
+
...req.tfyMetadata != null ? { tfyMetadata: req.tfyMetadata } : {}
|
|
152
|
+
});
|
|
153
|
+
const session = toSession(named);
|
|
154
|
+
cacheSessionType(session);
|
|
155
|
+
return session;
|
|
156
|
+
}
|
|
157
|
+
throw new Error("createSession requires agentName and/or agentSpec");
|
|
158
|
+
},
|
|
159
|
+
async listSessions(req) {
|
|
160
|
+
const page = await privateClient.listOwnedSessions({
|
|
161
|
+
limit: req?.limit,
|
|
162
|
+
order: req?.order,
|
|
163
|
+
pageToken: req?.pageToken,
|
|
164
|
+
startTimestamp: req?.startTimestamp,
|
|
165
|
+
endTimestamp: req?.endTimestamp,
|
|
166
|
+
...req?.agentName != null ? { agentName: req.agentName } : {}
|
|
167
|
+
});
|
|
168
|
+
const result = await toListResult(page, (s) => toSession(s));
|
|
169
|
+
for (const session of result.data) {
|
|
170
|
+
cacheSessionType(session);
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
},
|
|
174
|
+
async getSession({ sessionId }) {
|
|
175
|
+
const raw = await getSessionObj(sessionId);
|
|
176
|
+
const session = toSession(raw);
|
|
177
|
+
cacheSessionType(session);
|
|
178
|
+
return session;
|
|
179
|
+
},
|
|
180
|
+
async updateSession(req) {
|
|
181
|
+
const raw = await getSessionObj(req.sessionId);
|
|
182
|
+
if (!isDraft(raw)) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
"updateSession: session is not mutable (isMutable=false)"
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (req.agentSpec != null) {
|
|
188
|
+
await raw.update({
|
|
189
|
+
agentSpec: normalizeAgentSpecForGateway(req.agentSpec)
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return toSession(raw);
|
|
193
|
+
},
|
|
194
|
+
createTurn(req) {
|
|
195
|
+
return (async function* () {
|
|
196
|
+
const session = await getSessionObj(req.sessionId);
|
|
197
|
+
const prepared = session.prepareTurn({
|
|
198
|
+
input: req.input,
|
|
199
|
+
previousTurnId: req.previousTurnId ?? "auto"
|
|
200
|
+
});
|
|
201
|
+
yield* prepared.execute(
|
|
202
|
+
{ stream: true },
|
|
203
|
+
{
|
|
204
|
+
...req.abortSignal != null ? { abortSignal: req.abortSignal } : {},
|
|
205
|
+
...req.headers != null ? { headers: req.headers } : {}
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
})();
|
|
209
|
+
},
|
|
210
|
+
async cancelSession({ sessionId }) {
|
|
211
|
+
await (await getSessionObj(sessionId)).cancel();
|
|
212
|
+
},
|
|
213
|
+
async deleteSession({ sessionId }) {
|
|
214
|
+
if (opts.deleteSession == null) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
"deleteSession is not on the gateway SDK. Pass deleteSession to createTrueFoundryChatServer."
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
await opts.deleteSession({ sessionId });
|
|
220
|
+
},
|
|
221
|
+
// The runtime's signature offers `order`, but the gateway's listTurns
|
|
222
|
+
// takes no such param — forwarding it silently did nothing.
|
|
223
|
+
async listTurns({ sessionId, limit, pageToken }) {
|
|
224
|
+
const raw = await getSessionObj(sessionId);
|
|
225
|
+
const page = await raw.listTurns({
|
|
226
|
+
...limit != null ? { limit } : {},
|
|
227
|
+
...pageToken != null ? { pageToken } : {}
|
|
228
|
+
});
|
|
229
|
+
return toListResult(page, (turn) => toTurn(turn));
|
|
230
|
+
},
|
|
231
|
+
async getTurn({ sessionId, turnId }) {
|
|
232
|
+
const raw = await getSessionObj(sessionId);
|
|
233
|
+
return toTurn(await raw.getTurn({ turnId }));
|
|
234
|
+
},
|
|
235
|
+
async listEvents({ sessionId, pageToken, lastTurnId, limit }) {
|
|
236
|
+
const raw = await getSessionObj(sessionId);
|
|
237
|
+
const page = await raw.listEvents({
|
|
238
|
+
...limit != null ? { limit } : {},
|
|
239
|
+
...pageToken != null ? { pageToken } : {},
|
|
240
|
+
...lastTurnId != null ? { lastTurnId } : {}
|
|
241
|
+
});
|
|
242
|
+
return toListResult(
|
|
243
|
+
page,
|
|
244
|
+
(item) => item
|
|
245
|
+
);
|
|
246
|
+
},
|
|
247
|
+
async listTurnEvents({ sessionId, turnId, limit, pageToken, order }) {
|
|
248
|
+
const raw = await getSessionObj(sessionId);
|
|
249
|
+
const turn = await raw.getTurn({ turnId });
|
|
250
|
+
const page = await turn.listEvents({
|
|
251
|
+
...limit != null ? { limit } : {},
|
|
252
|
+
...pageToken != null ? { pageToken } : {},
|
|
253
|
+
...order != null ? { order } : {}
|
|
254
|
+
});
|
|
255
|
+
return toListResult(page, (event) => event);
|
|
256
|
+
},
|
|
257
|
+
async *subscribeToTurn({
|
|
258
|
+
sessionId,
|
|
259
|
+
turnId,
|
|
260
|
+
afterSequenceNumber,
|
|
261
|
+
abortSignal
|
|
262
|
+
}) {
|
|
263
|
+
const raw = await getSessionObj(sessionId);
|
|
264
|
+
const turn = await raw.getTurn({ turnId });
|
|
265
|
+
yield* turn.stream(
|
|
266
|
+
afterSequenceNumber != null ? { afterSequenceNumber } : {},
|
|
267
|
+
abortSignal != null ? { abortSignal } : {}
|
|
268
|
+
);
|
|
269
|
+
},
|
|
270
|
+
async downloadSandboxFile(sandboxId, req) {
|
|
271
|
+
const response = await privateClient.downloadSandboxFile(
|
|
272
|
+
sandboxId,
|
|
273
|
+
req
|
|
274
|
+
);
|
|
275
|
+
return await response.blob();
|
|
276
|
+
},
|
|
277
|
+
getGatewayClients: () => ({ client, privateClient })
|
|
278
|
+
};
|
|
279
|
+
return server;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/plugins/truefoundry-agent-server-adapter/cp.ts
|
|
283
|
+
function trimSlash(url) {
|
|
284
|
+
return url.replace(/\/+$/, "");
|
|
285
|
+
}
|
|
286
|
+
async function cpFetch(opts, path, init) {
|
|
287
|
+
const url = `${trimSlash(opts.cpURL)}${path.startsWith("/") ? path : `/${path}`}`;
|
|
288
|
+
const res = await fetch(url, {
|
|
289
|
+
...init,
|
|
290
|
+
headers: {
|
|
291
|
+
Authorization: `Bearer ${opts.apiKey}`,
|
|
292
|
+
Accept: "application/json",
|
|
293
|
+
...init?.headers ?? {}
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
if (!res.ok) {
|
|
297
|
+
throw new Error(`CP ${res.status} ${res.statusText}: ${path}`);
|
|
298
|
+
}
|
|
299
|
+
return await res.json();
|
|
300
|
+
}
|
|
301
|
+
function joinCpPath(cpURL, path) {
|
|
302
|
+
const base = trimSlash(cpURL);
|
|
303
|
+
const p = path.startsWith("/") ? path : `/${path}`;
|
|
304
|
+
return `${base}${p}`;
|
|
305
|
+
}
|
|
306
|
+
async function resolveGatewayURL(opts) {
|
|
307
|
+
if (opts.gatewayURL != null && opts.gatewayURL !== "") {
|
|
308
|
+
return opts.gatewayURL;
|
|
309
|
+
}
|
|
310
|
+
const session = await cpFetch(opts, "/api/svc/v1/session");
|
|
311
|
+
const tenantName = session.user?.tenantName ?? session.env?.TENANT_NAME ?? session.data?.user?.tenantName;
|
|
312
|
+
if (tenantName == null || tenantName === "") {
|
|
313
|
+
throw new Error(
|
|
314
|
+
"resolveGatewayURL: /api/svc/v1/session did not return user.tenantName"
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
const llmPrefix = trimSlash(session.env?.LLM_GATEWAY_URL ?? "/api/llm");
|
|
318
|
+
return joinCpPath(opts.cpURL, `${llmPrefix}/${tenantName}`);
|
|
319
|
+
}
|
|
320
|
+
function isChatModel(row) {
|
|
321
|
+
const types = row.types;
|
|
322
|
+
if (types == null || types.length === 0) return true;
|
|
323
|
+
return types.includes("chat");
|
|
324
|
+
}
|
|
325
|
+
function toModelEntry(row) {
|
|
326
|
+
if (!isChatModel(row)) return null;
|
|
327
|
+
const apiModel = row.model_fqn ?? row.id;
|
|
328
|
+
const name = row.name;
|
|
329
|
+
if (apiModel == null || apiModel === "" || name == null || name === "") {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
const provider = row.provider ?? "unknown";
|
|
333
|
+
const modelId = row.model_id ?? name;
|
|
334
|
+
return {
|
|
335
|
+
name,
|
|
336
|
+
provider,
|
|
337
|
+
apiModel,
|
|
338
|
+
modelId,
|
|
339
|
+
...row.provider_account_name != null ? { providerAccount: row.provider_account_name } : {},
|
|
340
|
+
id: apiModel
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function normalizeEnabledModels(raw) {
|
|
344
|
+
if (raw == null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
345
|
+
return [];
|
|
346
|
+
}
|
|
347
|
+
const out = [];
|
|
348
|
+
const seen = /* @__PURE__ */ new Set();
|
|
349
|
+
function pushRow(row) {
|
|
350
|
+
const entry = toModelEntry(row);
|
|
351
|
+
if (entry == null || seen.has(entry.apiModel)) return;
|
|
352
|
+
seen.add(entry.apiModel);
|
|
353
|
+
out.push(entry);
|
|
354
|
+
}
|
|
355
|
+
for (const level1 of Object.values(raw)) {
|
|
356
|
+
if (Array.isArray(level1)) {
|
|
357
|
+
for (const row of level1) {
|
|
358
|
+
if (row != null && typeof row === "object") {
|
|
359
|
+
pushRow(row);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
if (level1 == null || typeof level1 !== "object") continue;
|
|
365
|
+
for (const level2 of Object.values(level1)) {
|
|
366
|
+
if (!Array.isArray(level2)) continue;
|
|
367
|
+
for (const row of level2) {
|
|
368
|
+
if (row != null && typeof row === "object") {
|
|
369
|
+
pushRow(row);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return out;
|
|
375
|
+
}
|
|
376
|
+
async function listEnabledModels(opts) {
|
|
377
|
+
const raw = await cpFetch(
|
|
378
|
+
opts,
|
|
379
|
+
"/api/svc/v1/llm-gateway/model/enabled"
|
|
380
|
+
);
|
|
381
|
+
return normalizeEnabledModels(raw);
|
|
382
|
+
}
|
|
383
|
+
function normalizeAgentSkills(raw) {
|
|
384
|
+
const data = raw != null && typeof raw === "object" && Array.isArray(raw.data) ? raw.data : [];
|
|
385
|
+
const out = [];
|
|
386
|
+
for (const row of data) {
|
|
387
|
+
const fqn = row.latest_version?.fqn;
|
|
388
|
+
const name = row.name;
|
|
389
|
+
if (fqn == null || fqn === "" || name == null || name === "") continue;
|
|
390
|
+
const description = row.latest_version?.manifest?.source?.description;
|
|
391
|
+
out.push({
|
|
392
|
+
id: fqn,
|
|
393
|
+
name,
|
|
394
|
+
fqn,
|
|
395
|
+
...description != null ? { description } : {}
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
return out;
|
|
399
|
+
}
|
|
400
|
+
async function listAgentSkills(opts) {
|
|
401
|
+
const raw = await cpFetch(
|
|
402
|
+
opts,
|
|
403
|
+
"/api/ml/v1/agent-skills?include_empty_agent_skills=false"
|
|
404
|
+
);
|
|
405
|
+
return normalizeAgentSkills(raw);
|
|
406
|
+
}
|
|
407
|
+
function normalizeMcpServers(raw) {
|
|
408
|
+
const data = raw != null && typeof raw === "object" && Array.isArray(raw.data) ? raw.data : [];
|
|
409
|
+
const out = [];
|
|
410
|
+
const seen = /* @__PURE__ */ new Set();
|
|
411
|
+
for (const row of data) {
|
|
412
|
+
const mcpName = row.name;
|
|
413
|
+
if (mcpName == null || mcpName === "" || seen.has(mcpName)) continue;
|
|
414
|
+
seen.add(mcpName);
|
|
415
|
+
const description = row.manifest?.description;
|
|
416
|
+
const authenticated = row.authStatus?.status === "authenticated";
|
|
417
|
+
out.push({
|
|
418
|
+
id: mcpName,
|
|
419
|
+
name: mcpName,
|
|
420
|
+
mcpName,
|
|
421
|
+
...description != null ? { description } : {},
|
|
422
|
+
...row.id != null ? { serverId: row.id } : { serverId: null },
|
|
423
|
+
authenticated
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
return out;
|
|
427
|
+
}
|
|
428
|
+
async function listMcpServers(opts) {
|
|
429
|
+
const raw = await cpFetch(opts, "/api/svc/v1/mcp-servers");
|
|
430
|
+
return normalizeMcpServers(raw);
|
|
431
|
+
}
|
|
432
|
+
function normalizeAgents(raw) {
|
|
433
|
+
const data = raw != null && typeof raw === "object" && Array.isArray(raw.data) ? raw.data : [];
|
|
434
|
+
const out = [];
|
|
435
|
+
for (const row of data) {
|
|
436
|
+
if (row.name == null || row.name === "") continue;
|
|
437
|
+
out.push({ name: row.name });
|
|
438
|
+
}
|
|
439
|
+
return out;
|
|
440
|
+
}
|
|
441
|
+
async function listAgents(opts, req) {
|
|
442
|
+
const limit = req?.limit ?? 50;
|
|
443
|
+
const offset = req?.offset ?? 0;
|
|
444
|
+
const namePrefix = req?.query ?? "";
|
|
445
|
+
const qs = new URLSearchParams({
|
|
446
|
+
type: "truefoundry-agent",
|
|
447
|
+
limit: String(limit),
|
|
448
|
+
offset: String(offset),
|
|
449
|
+
namePrefix
|
|
450
|
+
});
|
|
451
|
+
const raw = await cpFetch(
|
|
452
|
+
opts,
|
|
453
|
+
`/api/svc/v1/agents?${qs.toString()}`
|
|
454
|
+
);
|
|
455
|
+
return normalizeAgents(raw);
|
|
456
|
+
}
|
|
457
|
+
var SAVE_AGENT_METADATA_TAGS = {
|
|
458
|
+
agent: "tfy-ai-gateway-agent",
|
|
459
|
+
TFY_ALPHA_ENABLE_OPENUI: "true",
|
|
460
|
+
TFY_ALPHA_ENABLE_ASK_USER: "true",
|
|
461
|
+
TFY_ALPHA_ENABLE_ASK_SECRET: "true",
|
|
462
|
+
TFY_ALPHA_CONTEXT_MANAGEMENT: '{"large_tool_response":{"individual_tool_response_token_threshold":8000}}',
|
|
463
|
+
TFY_ALPHA_ENABLE_FILE_DOWNLOAD: "true"
|
|
464
|
+
};
|
|
465
|
+
var SAVE_AGENT_COLLABORATORS = [
|
|
466
|
+
{ subject: "team:everyone", role_id: "agent-access" }
|
|
467
|
+
];
|
|
468
|
+
function camelToSnakeKey(key) {
|
|
469
|
+
return key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
470
|
+
}
|
|
471
|
+
function toSnakeCaseDeep(value) {
|
|
472
|
+
if (Array.isArray(value)) {
|
|
473
|
+
return value.map(toSnakeCaseDeep);
|
|
474
|
+
}
|
|
475
|
+
if (value != null && typeof value === "object") {
|
|
476
|
+
const out = {};
|
|
477
|
+
for (const [k, v] of Object.entries(value)) {
|
|
478
|
+
out[camelToSnakeKey(k)] = toSnakeCaseDeep(v);
|
|
479
|
+
}
|
|
480
|
+
return out;
|
|
481
|
+
}
|
|
482
|
+
return value;
|
|
483
|
+
}
|
|
484
|
+
function mcpMountForCp(mount) {
|
|
485
|
+
const snake = toSnakeCaseDeep(mount);
|
|
486
|
+
if (snake.type === "truefoundry-mcp-registry") {
|
|
487
|
+
return {
|
|
488
|
+
...snake,
|
|
489
|
+
enable_tools: snake.enable_tools ?? ["@all"],
|
|
490
|
+
preload: snake.preload ?? false
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
return snake;
|
|
494
|
+
}
|
|
495
|
+
function skillMountForCp(mount) {
|
|
496
|
+
const snake = toSnakeCaseDeep(mount);
|
|
497
|
+
if (snake.type === "truefoundry-skills-registry") {
|
|
498
|
+
return {
|
|
499
|
+
...snake,
|
|
500
|
+
preload: snake.preload ?? false
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
return snake;
|
|
504
|
+
}
|
|
505
|
+
function buildSaveAgentManifest(agentName, agentSpec) {
|
|
506
|
+
const spec = normalizeAgentSpecForGateway(agentSpec);
|
|
507
|
+
const mcpServers = (spec.mcpServers ?? []).map(mcpMountForCp);
|
|
508
|
+
const skills = (spec.skills ?? []).map(skillMountForCp);
|
|
509
|
+
const rawDescription = agentSpec.description;
|
|
510
|
+
const description = typeof rawDescription === "string" ? rawDescription : "";
|
|
511
|
+
return {
|
|
512
|
+
type: "truefoundry-agent",
|
|
513
|
+
name: agentName,
|
|
514
|
+
description,
|
|
515
|
+
model: toSnakeCaseDeep(spec.model),
|
|
516
|
+
metadata_tags: { ...SAVE_AGENT_METADATA_TAGS },
|
|
517
|
+
collaborators: [...SAVE_AGENT_COLLABORATORS],
|
|
518
|
+
...spec.instructions != null ? { instructions: spec.instructions } : {},
|
|
519
|
+
...spec.config != null ? { config: toSnakeCaseDeep(spec.config) } : {},
|
|
520
|
+
...mcpServers.length > 0 ? { mcp_servers: mcpServers } : {},
|
|
521
|
+
...skills.length > 0 ? { skills } : {}
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
async function saveAgent(opts, req) {
|
|
525
|
+
const manifest = buildSaveAgentManifest(req.agentName, req.agentSpec);
|
|
526
|
+
return cpFetch(opts, "/api/svc/v1/agents", {
|
|
527
|
+
method: "PUT",
|
|
528
|
+
headers: { "Content-Type": "application/json" },
|
|
529
|
+
body: JSON.stringify({ manifest })
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/plugins/truefoundry-agent-server-adapter/createTrueFoundryAgentUIServer.ts
|
|
534
|
+
function credentialsKey(opts) {
|
|
535
|
+
return `${opts.apiKey}\0${opts.cpURL}\0${opts.gatewayURL ?? ""}`;
|
|
536
|
+
}
|
|
537
|
+
var inflight = /* @__PURE__ */ new Map();
|
|
538
|
+
async function createTrueFoundryAgentUIServer(opts) {
|
|
539
|
+
const key = credentialsKey(opts);
|
|
540
|
+
const existing = inflight.get(key);
|
|
541
|
+
if (existing != null) {
|
|
542
|
+
return existing;
|
|
543
|
+
}
|
|
544
|
+
const promise = (async () => {
|
|
545
|
+
const baseUrl = await resolveGatewayURL(opts);
|
|
546
|
+
const chat = createTrueFoundryChatServer({
|
|
547
|
+
apiKey: opts.apiKey,
|
|
548
|
+
baseUrl
|
|
549
|
+
});
|
|
550
|
+
const cp = { apiKey: opts.apiKey, cpURL: opts.cpURL };
|
|
551
|
+
const server = {
|
|
552
|
+
...chat,
|
|
553
|
+
getModels: () => listEnabledModels(cp),
|
|
554
|
+
getSkills: () => listAgentSkills(cp),
|
|
555
|
+
getMcp: () => listMcpServers(cp),
|
|
556
|
+
searchAgents: (req) => listAgents(cp, req),
|
|
557
|
+
saveAgent: (req) => saveAgent(cp, req)
|
|
558
|
+
};
|
|
559
|
+
return server;
|
|
560
|
+
})();
|
|
561
|
+
inflight.set(key, promise);
|
|
562
|
+
try {
|
|
563
|
+
return await promise;
|
|
564
|
+
} finally {
|
|
565
|
+
inflight.delete(key);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// src/plugins/truefoundry-agent-server-adapter/guards.ts
|
|
570
|
+
function isRecord2(value) {
|
|
571
|
+
return typeof value === "object" && value !== null;
|
|
572
|
+
}
|
|
573
|
+
function hasNumbers(value, keys) {
|
|
574
|
+
return isRecord2(value) && keys.every((key) => typeof value[key] === "number");
|
|
575
|
+
}
|
|
576
|
+
function isTfySystemToolInfo(toolInfo) {
|
|
577
|
+
return isRecord2(toolInfo) && toolInfo.type === "truefoundry-system" && typeof toolInfo.name === "string";
|
|
578
|
+
}
|
|
579
|
+
function isTfyMcpToolInfo(toolInfo) {
|
|
580
|
+
return isRecord2(toolInfo) && toolInfo.type === "mcp" && typeof toolInfo.name === "string" && typeof toolInfo.serverId === "string" && typeof toolInfo.serverName === "string";
|
|
581
|
+
}
|
|
582
|
+
function isTfyToolInfo(toolInfo) {
|
|
583
|
+
return isTfySystemToolInfo(toolInfo) || isTfyMcpToolInfo(toolInfo);
|
|
584
|
+
}
|
|
585
|
+
var USAGE_BREAKDOWN_KEYS = [
|
|
586
|
+
"harness",
|
|
587
|
+
"skills",
|
|
588
|
+
"instructions",
|
|
589
|
+
"toolDefinitions",
|
|
590
|
+
"messages"
|
|
591
|
+
];
|
|
592
|
+
function getTfyUsage(source) {
|
|
593
|
+
const usage = source?.usage;
|
|
594
|
+
if (!hasNumbers(usage, ["inputTokens", "outputTokens"])) {
|
|
595
|
+
return void 0;
|
|
596
|
+
}
|
|
597
|
+
if (!hasNumbers(usage.inputTokensBreakdown, USAGE_BREAKDOWN_KEYS)) {
|
|
598
|
+
return void 0;
|
|
599
|
+
}
|
|
600
|
+
return usage;
|
|
601
|
+
}
|
|
602
|
+
function getTfyThreadState(event) {
|
|
603
|
+
const state = event?.state;
|
|
604
|
+
if (!isRecord2(state)) {
|
|
605
|
+
return void 0;
|
|
606
|
+
}
|
|
607
|
+
if (state.status === "done" && isRecord2(state.output)) {
|
|
608
|
+
return state;
|
|
609
|
+
}
|
|
610
|
+
if (state.status === "error" && typeof state.error === "string") {
|
|
611
|
+
return state;
|
|
612
|
+
}
|
|
613
|
+
return void 0;
|
|
614
|
+
}
|
|
615
|
+
function getTfyMcpInitServers(event) {
|
|
616
|
+
const servers = event?.mcpServers;
|
|
617
|
+
if (!Array.isArray(servers)) {
|
|
618
|
+
return void 0;
|
|
619
|
+
}
|
|
620
|
+
const valid = servers.every(
|
|
621
|
+
(server) => isRecord2(server) && typeof server.id === "string" && typeof server.name === "string"
|
|
622
|
+
);
|
|
623
|
+
return valid ? servers : void 0;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
export {
|
|
627
|
+
createTrueFoundryChatServer,
|
|
628
|
+
createTrueFoundryAgentUIServer,
|
|
629
|
+
isTfySystemToolInfo,
|
|
630
|
+
isTfyMcpToolInfo,
|
|
631
|
+
isTfyToolInfo,
|
|
632
|
+
getTfyUsage,
|
|
633
|
+
getTfyThreadState,
|
|
634
|
+
getTfyMcpInitServers
|
|
635
|
+
};
|
|
636
|
+
//# sourceMappingURL=chunk-CXBZ6WLZ.js.map
|