diff --git a/.changeset/20260911065947-regenerate-sdk-from-openapi.md b/.changeset/20260911065947-regenerate-sdk-from-openapi.md
new file mode 100644
index 000000000..efd8ff00f
--- /dev/null
+++ b/.changeset/20260911065947-regenerate-sdk-from-openapi.md
@@ -0,0 +1,5 @@
+---
+"@truefoundry/trueforge-sdk": patch
+---
+
+Regenerate SDK from updated OpenAPI spec.
diff --git a/.changeset/agent-description-ui.md b/.changeset/agent-description-ui.md
new file mode 100644
index 000000000..41547498f
--- /dev/null
+++ b/.changeset/agent-description-ui.md
@@ -0,0 +1,5 @@
+---
+"@truefoundry/trueforge-ui": patch
+---
+
+Round-trip agent description through save/load and show it in the library and agent details.
diff --git a/.changeset/agent-description.md b/.changeset/agent-description.md
new file mode 100644
index 000000000..59884d1b6
--- /dev/null
+++ b/.changeset/agent-description.md
@@ -0,0 +1,5 @@
+---
+"@truefoundry/trueforge": patch
+---
+
+Persist top-level agent description and sync it to ServiceFoundry on create/update.
diff --git a/.github/fern/openapi/openapi.json b/.github/fern/openapi/openapi.json
index 9b076745f..357154849 100644
--- a/.github/fern/openapi/openapi.json
+++ b/.github/fern/openapi/openapi.json
@@ -29,6 +29,11 @@
"created_by_subject": {
"$ref": "#/components/schemas/CreatedBySubject"
},
+ "description": {
+ "maxLength": 1024,
+ "minLength": 1,
+ "type": "string"
+ },
"id": {
"description": "Immutable server-generated agent identifier.",
"minLength": 1,
@@ -44,6 +49,7 @@
"required": [
"id",
"name",
+ "description",
"manifest",
"created_by_subject"
],
@@ -982,6 +988,11 @@
"CreateAgentRequest": {
"additionalProperties": false,
"properties": {
+ "description": {
+ "maxLength": 1024,
+ "minLength": 1,
+ "type": "string"
+ },
"manifest": {
"$ref": "#/components/schemas/AgentSpec"
},
@@ -991,6 +1002,7 @@
},
"required": [
"name",
+ "description",
"manifest"
],
"type": "object"
@@ -5330,6 +5342,11 @@
"UpdateAgentRequest": {
"additionalProperties": false,
"properties": {
+ "description": {
+ "maxLength": 1024,
+ "minLength": 1,
+ "type": "string"
+ },
"manifest": {
"$ref": "#/components/schemas/AgentSpec"
}
@@ -6370,7 +6387,7 @@
"x-fern-sdk-method-name": "get"
},
"put": {
- "description": "Replaces the manifest for an existing agent keyed by immutable `agent_id`.",
+ "description": "Update an existing agent by immutable id.",
"parameters": [
{
"description": "Immutable agent identifier.",
diff --git a/docs/openapi.json b/docs/openapi.json
index 9b076745f..357154849 100644
--- a/docs/openapi.json
+++ b/docs/openapi.json
@@ -29,6 +29,11 @@
"created_by_subject": {
"$ref": "#/components/schemas/CreatedBySubject"
},
+ "description": {
+ "maxLength": 1024,
+ "minLength": 1,
+ "type": "string"
+ },
"id": {
"description": "Immutable server-generated agent identifier.",
"minLength": 1,
@@ -44,6 +49,7 @@
"required": [
"id",
"name",
+ "description",
"manifest",
"created_by_subject"
],
@@ -982,6 +988,11 @@
"CreateAgentRequest": {
"additionalProperties": false,
"properties": {
+ "description": {
+ "maxLength": 1024,
+ "minLength": 1,
+ "type": "string"
+ },
"manifest": {
"$ref": "#/components/schemas/AgentSpec"
},
@@ -991,6 +1002,7 @@
},
"required": [
"name",
+ "description",
"manifest"
],
"type": "object"
@@ -5330,6 +5342,11 @@
"UpdateAgentRequest": {
"additionalProperties": false,
"properties": {
+ "description": {
+ "maxLength": 1024,
+ "minLength": 1,
+ "type": "string"
+ },
"manifest": {
"$ref": "#/components/schemas/AgentSpec"
}
@@ -6370,7 +6387,7 @@
"x-fern-sdk-method-name": "get"
},
"put": {
- "description": "Replaces the manifest for an existing agent keyed by immutable `agent_id`.",
+ "description": "Update an existing agent by immutable id.",
"parameters": [
{
"description": "Immutable agent identifier.",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 7df484c6f..693fa3a78 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -26,7 +26,7 @@
"@assistant-ui/core": "0.2.22",
"@assistant-ui/react": "0.14.27",
"@assistant-ui/store": "0.2.21",
- "@truefoundry/assistant-ui-runtime": "0.1.36",
+ "@truefoundry/assistant-ui-runtime": "0.1.37",
"@truefoundry/trueforge-sdk": "workspace:*",
"@truefoundry/trueforge-ui": "workspace:*",
"monaco-editor": "^0.52.0",
diff --git a/packages/trueforge-sdk/reference.md b/packages/trueforge-sdk/reference.md
index fe932a62d..98c1d13f3 100644
--- a/packages/trueforge-sdk/reference.md
+++ b/packages/trueforge-sdk/reference.md
@@ -158,6 +158,7 @@ Creates an agent and allocates an immutable id. Fails if `name` is already taken
```typescript
await client.agents.create({
+ description: "description",
manifest: {
model: {
name: "name"
@@ -275,7 +276,7 @@ await client.agents.get("agent_id");
-
-Replaces the manifest for an existing agent keyed by immutable `agent_id`.
+Update an existing agent by immutable id.
diff --git a/packages/trueforge-sdk/src/api/resources/agents/client/Client.ts b/packages/trueforge-sdk/src/api/resources/agents/client/Client.ts
index c2aece33d..5a3ff0a29 100644
--- a/packages/trueforge-sdk/src/api/resources/agents/client/Client.ts
+++ b/packages/trueforge-sdk/src/api/resources/agents/client/Client.ts
@@ -151,6 +151,7 @@ export class AgentsClient {
*
* @example
* await client.agents.create({
+ * description: "description",
* manifest: {
* model: {
* name: "name"
@@ -346,7 +347,7 @@ export class AgentsClient {
}
/**
- * Replaces the manifest for an existing agent keyed by immutable `agent_id`.
+ * Update an existing agent by immutable id.
*
* @param {string} agent_id - Immutable agent identifier.
* @param {TrueForge.UpdateAgentRequest} request
diff --git a/packages/trueforge-sdk/src/api/resources/agents/client/requests/CreateAgentRequest.ts b/packages/trueforge-sdk/src/api/resources/agents/client/requests/CreateAgentRequest.ts
index 0648583e5..bec02b543 100644
--- a/packages/trueforge-sdk/src/api/resources/agents/client/requests/CreateAgentRequest.ts
+++ b/packages/trueforge-sdk/src/api/resources/agents/client/requests/CreateAgentRequest.ts
@@ -5,6 +5,7 @@ import type * as TrueForge from "../../../../index.js";
/**
* @example
* {
+ * description: "description",
* manifest: {
* model: {
* name: "name"
@@ -14,6 +15,7 @@ import type * as TrueForge from "../../../../index.js";
* }
*/
export interface CreateAgentRequest {
+ description: string;
manifest: TrueForge.AgentSpec;
name: TrueForge.ResourceName;
}
diff --git a/packages/trueforge-sdk/src/api/resources/agents/client/requests/UpdateAgentRequest.ts b/packages/trueforge-sdk/src/api/resources/agents/client/requests/UpdateAgentRequest.ts
index b92a833d5..4b4d7470b 100644
--- a/packages/trueforge-sdk/src/api/resources/agents/client/requests/UpdateAgentRequest.ts
+++ b/packages/trueforge-sdk/src/api/resources/agents/client/requests/UpdateAgentRequest.ts
@@ -13,5 +13,6 @@ import type * as TrueForge from "../../../../index.js";
* }
*/
export interface UpdateAgentRequest {
+ description?: string;
manifest: TrueForge.AgentSpec;
}
diff --git a/packages/trueforge-sdk/src/api/types/Agent.ts b/packages/trueforge-sdk/src/api/types/Agent.ts
index a912c1c39..497c56a02 100644
--- a/packages/trueforge-sdk/src/api/types/Agent.ts
+++ b/packages/trueforge-sdk/src/api/types/Agent.ts
@@ -4,6 +4,7 @@ import type * as TrueForge from "../index.js";
export interface Agent {
createdBySubject: TrueForge.CreatedBySubject;
+ description: string;
/** Immutable server-generated agent identifier. */
id: string;
manifest: TrueForge.AgentSpec;
diff --git a/packages/trueforge-sdk/src/serialization/resources/agents/client/requests/CreateAgentRequest.ts b/packages/trueforge-sdk/src/serialization/resources/agents/client/requests/CreateAgentRequest.ts
index 27456816a..1fff2647c 100644
--- a/packages/trueforge-sdk/src/serialization/resources/agents/client/requests/CreateAgentRequest.ts
+++ b/packages/trueforge-sdk/src/serialization/resources/agents/client/requests/CreateAgentRequest.ts
@@ -10,12 +10,14 @@ export const CreateAgentRequest: core.serialization.Schema<
serializers.CreateAgentRequest.Raw,
TrueForge.CreateAgentRequest
> = core.serialization.object({
+ description: core.serialization.string(),
manifest: AgentSpec,
name: ResourceName,
});
export declare namespace CreateAgentRequest {
export interface Raw {
+ description: string;
manifest: AgentSpec.Raw;
name: ResourceName.Raw;
}
diff --git a/packages/trueforge-sdk/src/serialization/resources/agents/client/requests/UpdateAgentRequest.ts b/packages/trueforge-sdk/src/serialization/resources/agents/client/requests/UpdateAgentRequest.ts
index 5ead63d5b..e9431c780 100644
--- a/packages/trueforge-sdk/src/serialization/resources/agents/client/requests/UpdateAgentRequest.ts
+++ b/packages/trueforge-sdk/src/serialization/resources/agents/client/requests/UpdateAgentRequest.ts
@@ -9,11 +9,13 @@ export const UpdateAgentRequest: core.serialization.Schema<
serializers.UpdateAgentRequest.Raw,
TrueForge.UpdateAgentRequest
> = core.serialization.object({
+ description: core.serialization.string().optional(),
manifest: AgentSpec,
});
export declare namespace UpdateAgentRequest {
export interface Raw {
+ description?: string | null;
manifest: AgentSpec.Raw;
}
}
diff --git a/packages/trueforge-sdk/src/serialization/types/Agent.ts b/packages/trueforge-sdk/src/serialization/types/Agent.ts
index 21ce3103a..031634495 100644
--- a/packages/trueforge-sdk/src/serialization/types/Agent.ts
+++ b/packages/trueforge-sdk/src/serialization/types/Agent.ts
@@ -10,6 +10,7 @@ import { ResourceName } from "./ResourceName.js";
export const Agent: core.serialization.ObjectSchema = core.serialization.object(
{
createdBySubject: core.serialization.property("created_by_subject", CreatedBySubject),
+ description: core.serialization.string(),
id: core.serialization.string(),
manifest: AgentSpec,
name: ResourceName,
@@ -19,6 +20,7 @@ export const Agent: core.serialization.ObjectSchema {
subject_id: "subject_id",
subject_type: "subject_type",
},
+ description: "description",
id: "id",
manifest: { model: { name: "name" } },
name: "name",
@@ -41,6 +42,7 @@ describe("AgentsClient", () => {
subjectId: "subject_id",
subjectType: "subject_type",
},
+ description: "description",
id: "id",
manifest: {
model: {
@@ -93,7 +95,7 @@ describe("AgentsClient", () => {
test("create (1)", async () => {
const server = mockServerPool.createServer();
const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl });
- const rawRequestBody = { manifest: { model: { name: "name" } }, name: "name" };
+ const rawRequestBody = { description: "description", manifest: { model: { name: "name" } }, name: "name" };
const rawResponseBody = {
data: {
created_by_subject: {
@@ -101,6 +103,7 @@ describe("AgentsClient", () => {
subject_id: "subject_id",
subject_type: "subject_type",
},
+ description: "description",
id: "id",
manifest: {
instructions: "instructions",
@@ -124,6 +127,7 @@ describe("AgentsClient", () => {
.build();
const response = await client.agents.create({
+ description: "description",
manifest: {
model: {
name: "name",
@@ -138,6 +142,7 @@ describe("AgentsClient", () => {
subjectId: "subject_id",
subjectType: "subject_type",
},
+ description: "description",
id: "id",
manifest: {
instructions: "instructions",
@@ -172,7 +177,7 @@ describe("AgentsClient", () => {
test("create (2)", async () => {
const server = mockServerPool.createServer();
const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl });
- const rawRequestBody = { manifest: { model: { name: "x" } }, name: "xy" };
+ const rawRequestBody = { description: "x", manifest: { model: { name: "x" } }, name: "xy" };
const rawResponseBody = { error: { message: "message" } };
server
@@ -186,6 +191,7 @@ describe("AgentsClient", () => {
await expect(async () => {
return await client.agents.create({
+ description: "x",
manifest: {
model: {
name: "x",
@@ -199,7 +205,7 @@ describe("AgentsClient", () => {
test("create (3)", async () => {
const server = mockServerPool.createServer();
const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl });
- const rawRequestBody = { manifest: { model: { name: "x" } }, name: "xy" };
+ const rawRequestBody = { description: "x", manifest: { model: { name: "x" } }, name: "xy" };
const rawResponseBody = { error: { message: "message" } };
server
@@ -213,6 +219,7 @@ describe("AgentsClient", () => {
await expect(async () => {
return await client.agents.create({
+ description: "x",
manifest: {
model: {
name: "x",
@@ -226,7 +233,7 @@ describe("AgentsClient", () => {
test("create (4)", async () => {
const server = mockServerPool.createServer();
const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl });
- const rawRequestBody = { manifest: { model: { name: "x" } }, name: "xy" };
+ const rawRequestBody = { description: "x", manifest: { model: { name: "x" } }, name: "xy" };
const rawResponseBody = { error: { message: "message" } };
server
@@ -240,6 +247,7 @@ describe("AgentsClient", () => {
await expect(async () => {
return await client.agents.create({
+ description: "x",
manifest: {
model: {
name: "x",
@@ -261,6 +269,7 @@ describe("AgentsClient", () => {
subject_id: "subject_id",
subject_type: "subject_type",
},
+ description: "description",
id: "id",
manifest: {
instructions: "instructions",
@@ -290,6 +299,7 @@ describe("AgentsClient", () => {
subjectId: "subject_id",
subjectType: "subject_type",
},
+ description: "description",
id: "id",
manifest: {
instructions: "instructions",
@@ -351,6 +361,7 @@ describe("AgentsClient", () => {
subject_id: "subject_id",
subject_type: "subject_type",
},
+ description: "description",
id: "id",
manifest: {
instructions: "instructions",
@@ -387,6 +398,7 @@ describe("AgentsClient", () => {
subjectId: "subject_id",
subjectType: "subject_type",
},
+ description: "description",
id: "id",
manifest: {
instructions: "instructions",
diff --git a/packages/trueforge-ui/package.json b/packages/trueforge-ui/package.json
index c2000135a..1607b781f 100644
--- a/packages/trueforge-ui/package.json
+++ b/packages/trueforge-ui/package.json
@@ -95,7 +95,7 @@
"@openuidev/react-headless": "^0.9.4",
"@openuidev/react-lang": "^0.2.9",
"@openuidev/react-ui": "^0.13.2",
- "@truefoundry/assistant-ui-runtime": "0.1.36",
+ "@truefoundry/assistant-ui-runtime": "0.1.37",
"@truefoundry/trueforge-sdk": "workspace:*",
"chart.js": "^4.5.1",
"clsx": "^2.1.1",
diff --git a/packages/trueforge-ui/src/atoms/AgentOverflowMenu.tsx b/packages/trueforge-ui/src/atoms/AgentOverflowMenu.tsx
index c58d8352e..66ab7be54 100644
--- a/packages/trueforge-ui/src/atoms/AgentOverflowMenu.tsx
+++ b/packages/trueforge-ui/src/atoms/AgentOverflowMenu.tsx
@@ -20,6 +20,7 @@ export function cloneAgentName(agentName: string): string {
export type AgentOverflowMenuProps = {
agentName: string;
+ description?: string;
agentSpec?: AgentSpec;
/** Edit / Clone / Delete when composer is enabled. */
canMutate: boolean;
@@ -37,6 +38,7 @@ type PendingAction = 'clone' | 'delete' | null;
export function AgentOverflowMenu({
agentName,
+ description,
agentSpec,
canMutate,
canUse = true,
@@ -72,6 +74,7 @@ export function AgentOverflowMenu({
try {
await builder.saveAgent({
agentName: clonedName,
+ ...(description === undefined ? {} : { description }),
agentSpec,
intent: 'create',
});
diff --git a/packages/trueforge-ui/src/atoms/AgentsLibrary.tsx b/packages/trueforge-ui/src/atoms/AgentsLibrary.tsx
index df8b4ce1c..436e07639 100644
--- a/packages/trueforge-ui/src/atoms/AgentsLibrary.tsx
+++ b/packages/trueforge-ui/src/atoms/AgentsLibrary.tsx
@@ -189,10 +189,14 @@ export function AgentLibraryRow({
const skillsTitle = skillNames.length ? skillNames.join(', ') : `${skillsCount} skills`;
const hasConfiguration = modelLabel != null || skillsCount > 0 || mcpCount > 0;
const hasNoSchedules = scheduleSummary != null && scheduleSummary.count === 0;
+ const storedDescription = agent.description?.trim() || null;
+ // Create falls back to name when description is missing; don't echo it under the title.
+ const description = storedDescription != null && storedDescription !== agent.name ? storedDescription : null;
return (
-
+ {/* Fixed width so truncate works; 24rem = 1.5× the prior min-w-64 name column. */}
+
{onOpen == null ? (
{agent.name}
) : (
@@ -205,6 +209,15 @@ export function AgentLibraryRow({
{agent.name}
)}
+ {description ? (
+
+ {description}
+
+ ) : null}
{hasConfiguration ? (
@@ -276,6 +289,7 @@ export function AgentLibraryRow({
- Agent name
+ Agent name
Configuration
{showCreatedByColumn ? Created by : null}
{showSchedulesColumn ? Schedules : null}
diff --git a/packages/trueforge-ui/src/atoms/SaveAgentButton.tsx b/packages/trueforge-ui/src/atoms/SaveAgentButton.tsx
index e9c5c4fe8..c764dfb3e 100644
--- a/packages/trueforge-ui/src/atoms/SaveAgentButton.tsx
+++ b/packages/trueforge-ui/src/atoms/SaveAgentButton.tsx
@@ -86,6 +86,7 @@ function SaveAgentButtonContent({
const [open, setOpen] = useState(false);
const [intent, setIntent] = useState('create');
const [name, setName] = useState('');
+ const [description, setDescription] = useState('');
const [draftSpec, setDraftSpec] = useState(null);
const [saving, setSaving] = useState(false);
const [error, setError] = useState(null);
@@ -93,6 +94,7 @@ function SaveAgentButtonContent({
const close = () => {
if (saving) return;
setOpen(false);
+ setDescription('');
setDraftSpec(null);
setError(null);
};
@@ -111,6 +113,7 @@ function SaveAgentButtonContent({
const currentName = shell?.mode.status === 'active' ? (shell.mode.agentName ?? shell.mode.agentId ?? '') : '';
setIntent(currentName ? 'update' : 'create');
setName(currentName);
+ setDescription(currentName && shell?.mode.status === 'active' ? (shell.mode.description ?? '') : '');
setDraftSpec(cloneAgentSpec(latestAgentSpec));
setOpen(true);
};
@@ -120,11 +123,14 @@ function SaveAgentButtonContent({
if (intent === 'update' && !canManageAgent) return;
const normalizedName = name.trim();
if (!normalizedName || !draftSpec.model.name.trim()) return;
+ const normalizedDescription = description.trim();
+ if (!normalizedDescription) return;
setSaving(true);
setError(null);
try {
const result = await builder.saveAgent({
agentName: normalizedName,
+ ...(normalizedDescription ? { description: normalizedDescription } : {}),
agentSpec: draftSpec,
intent,
sessionId: draftSessionId,
@@ -133,10 +139,12 @@ function SaveAgentButtonContent({
shell?.bindMutableAgent({
agentId: result.agentId ?? normalizedName,
agentName: normalizedName,
+ ...(normalizedDescription ? { description: normalizedDescription } : {}),
agentSpec: draftSpec,
});
shell?.invalidateAgentsList();
setOpen(false);
+ setDescription('');
setDraftSpec(null);
} catch (caught) {
setError(getErrorMessage(caught, 'Could not save agent'));
@@ -180,11 +188,12 @@ function SaveAgentButtonContent({
void save()}
/>
diff --git a/packages/trueforge-ui/src/atoms/SaveAgentForm.tsx b/packages/trueforge-ui/src/atoms/SaveAgentForm.tsx
index 8c6d0f236..780aecebc 100644
--- a/packages/trueforge-ui/src/atoms/SaveAgentForm.tsx
+++ b/packages/trueforge-ui/src/atoms/SaveAgentForm.tsx
@@ -9,11 +9,12 @@ import { Button } from './primitives/Button.js';
export type SaveAgentFormProps = {
intent: 'create' | 'update';
name: string;
+ description: string;
spec: AgentSpec;
saving: boolean;
error: string | null;
onNameChange: (name: string) => void;
- onChange: (spec: AgentSpec) => void;
+ onDescriptionChange: (description: string) => void;
onCancel: () => void;
onSave: () => void;
};
@@ -21,10 +22,12 @@ export type SaveAgentFormProps = {
export function SaveAgentForm({
intent,
name,
+ description,
spec,
saving,
error,
onNameChange,
+ onDescriptionChange,
onCancel,
onSave,
}: SaveAgentFormProps) {
@@ -36,6 +39,8 @@ export function SaveAgentForm({
errorRef.current?.scrollIntoView?.({ block: 'nearest', behavior: 'smooth' });
}, [error]);
+ const canSave = name.trim() !== '' && spec.model.name.trim() !== '' && description.trim() !== '';
+
return (
@@ -50,18 +55,18 @@ export function SaveAgentForm({
/>
- {/* TODO: Uncomment the description field when the backend supports description */}
- {/*
*/}
+
{error ? (
Cancel
-
+
{saving ? 'Saving…' : 'Save changes'}
diff --git a/packages/trueforge-ui/src/atoms/agent-details/AgentDetailsHeader.tsx b/packages/trueforge-ui/src/atoms/agent-details/AgentDetailsHeader.tsx
index 617070bb2..3125c0431 100644
--- a/packages/trueforge-ui/src/atoms/agent-details/AgentDetailsHeader.tsx
+++ b/packages/trueforge-ui/src/atoms/agent-details/AgentDetailsHeader.tsx
@@ -38,6 +38,7 @@ export function AgentDetailsHeader({ agentId, detail, onBack }: AgentDetailsHead
isCreateAgent: true,
agentId: detail.agentId,
agentName: detail.name,
+ ...(detail.description === undefined ? {} : { description: detail.description }),
agentSpec: detail.agentSpec,
});
};
@@ -82,6 +83,7 @@ export function AgentDetailsHeader({ agentId, detail, onBack }: AgentDetailsHead
{detail != null ? (
-
-
-
-
- Instructions
-
- {instructions ? (
-
-
);
}
diff --git a/packages/trueforge-ui/src/atoms/agent-details/AgentOverviewCard.tsx b/packages/trueforge-ui/src/atoms/agent-details/AgentOverviewCard.tsx
index eb1ba46a1..97ebb8c99 100644
--- a/packages/trueforge-ui/src/atoms/agent-details/AgentOverviewCard.tsx
+++ b/packages/trueforge-ui/src/atoms/agent-details/AgentOverviewCard.tsx
@@ -5,7 +5,7 @@ export function AgentOverviewCard({ title, icon, count, children }: AgentOvervie
return (
-
+ {icon ? : null}
{title}
{count !== undefined ? ({count}) : null}
diff --git a/packages/trueforge-ui/src/atoms/agent-details/types.ts b/packages/trueforge-ui/src/atoms/agent-details/types.ts
index 147d5c6ec..b6e306d7d 100644
--- a/packages/trueforge-ui/src/atoms/agent-details/types.ts
+++ b/packages/trueforge-ui/src/atoms/agent-details/types.ts
@@ -158,7 +158,7 @@ export type AgentOverviewProps = {
export type AgentOverviewCardProps = {
title: string;
- icon: string;
+ icon?: string;
count?: number;
children: ReactNode;
};
diff --git a/packages/trueforge-ui/src/plugins/trueforge-agent-server-adapter/agentSessionsServer.ts b/packages/trueforge-ui/src/plugins/trueforge-agent-server-adapter/agentSessionsServer.ts
index cf9c827e4..2d40f6b83 100644
--- a/packages/trueforge-ui/src/plugins/trueforge-agent-server-adapter/agentSessionsServer.ts
+++ b/packages/trueforge-ui/src/plugins/trueforge-agent-server-adapter/agentSessionsServer.ts
@@ -52,6 +52,7 @@ export function createHarnessAgentSessionsServer(
return {
agentId: data.id,
name: data.name,
+ description: data.description,
agentSpec: toUiAgentSpec(data.manifest),
};
},
diff --git a/packages/trueforge-ui/src/plugins/trueforge-agent-server-adapter/builderServer.ts b/packages/trueforge-ui/src/plugins/trueforge-agent-server-adapter/builderServer.ts
index bdeeb1435..f00a7f7a0 100644
--- a/packages/trueforge-ui/src/plugins/trueforge-agent-server-adapter/builderServer.ts
+++ b/packages/trueforge-ui/src/plugins/trueforge-agent-server-adapter/builderServer.ts
@@ -57,6 +57,7 @@ function toLibraryEntry(agent: TrueForgeApi.Agent): AgentLibraryEntry {
return {
name: agent.name,
agentId: agent.id,
+ description: agent.description,
agentSpec: toUiAgentSpec(agent.manifest),
createdBySubject: agent.createdBySubject,
};
@@ -133,19 +134,29 @@ export function createHarnessBuilderServer(
return rows.map(toLibraryEntry);
},
- async saveAgent({ agentName, agentSpec, intent }) {
- // TODO: TrueForge currently drops AgentSpec.description until its schema supports it.
+ async saveAgent({ agentName, description: descriptionRaw, agentSpec, intent }) {
const manifest = toHarnessAgentSpec(agentSpec);
+ const description = descriptionRaw?.trim();
if (intent === 'update') {
const agents = await drainAgentsList(client);
const existing = agents.find(agent => agent.name === agentName);
if (!existing) {
return {};
}
- await client.agents.update(existing.id, { manifest });
+ // Omit empty description on update so a reloaded draft (no description in session
+ // manifest) preserves the stored value instead of failing min(1).
+ await client.agents.update(existing.id, {
+ ...(description ? { description } : {}),
+ manifest,
+ });
return { agentId: existing.id };
}
- const created = await client.agents.create({ name: agentName, manifest });
+ // Create requires description; fall back to name for clone of pre-description agents.
+ const created = await client.agents.create({
+ name: agentName,
+ description: description || agentName,
+ manifest,
+ });
return { agentId: created.data.id };
},
diff --git a/packages/trueforge-ui/src/server/ShellModeContext.tsx b/packages/trueforge-ui/src/server/ShellModeContext.tsx
index 9d8901953..0d06fdf8f 100644
--- a/packages/trueforge-ui/src/server/ShellModeContext.tsx
+++ b/packages/trueforge-ui/src/server/ShellModeContext.tsx
@@ -50,6 +50,8 @@ export type ShellMode =
agentId?: string;
/** Display / welcome label (often same as agentId). */
agentName?: string;
+ /** Published-agent description, kept outside the executable agent spec. */
+ description?: string;
/** Seed for mutable (draft) runtime. */
agentSpec?: AgentSpec;
locked: boolean;
@@ -64,6 +66,7 @@ export type SelectLibraryAgentRequest = {
isCreateAgent?: boolean;
agentId?: string;
agentName?: string;
+ description?: string;
agentSpec?: AgentSpec;
};
@@ -111,7 +114,7 @@ type ShellModeContextValue = {
* Attach identity + agentSpec to the *current* mutable chat without remounting.
* Used after `saveAgent` so the same draft session continues as an editable agent.
*/
- bindMutableAgent: (req: { agentId: string; agentName: string; agentSpec: AgentSpec }) => void;
+ bindMutableAgent: (req: { agentId: string; agentName: string; description?: string; agentSpec: AgentSpec }) => void;
/** @deprecated Prefer `selectLibraryAgent({ isMutable: false, agentName })`. */
selectAgent: (agentName: string) => void;
/** Open a simple New Chat (mutable, no agent-builder chrome). */
@@ -411,6 +414,7 @@ export function ShellModeProvider({
isCreateAgent,
agentId: req.agentId,
agentName: req.agentName,
+ description: req.description,
agentSpec: req.agentSpec ?? (kind === 'agent' ? agentSeedRef.current : chatSeedRef.current),
locked: false,
});
@@ -451,7 +455,7 @@ export function ShellModeProvider({
);
const bindMutableAgent = useCallback(
- (req: { agentId: string; agentName: string; agentSpec: AgentSpec }) => {
+ (req: { agentId: string; agentName: string; description?: string; agentSpec: AgentSpec }) => {
if (!isComposerEnabled) return;
setMode(prev => {
if (prev.status !== 'active' || !prev.isMutable) return prev;
@@ -461,6 +465,7 @@ export function ShellModeProvider({
isCreateAgent: true,
agentId: req.agentId,
agentName: req.agentName,
+ description: req.description,
agentSpec: req.agentSpec,
locked: false,
};
diff --git a/packages/trueforge-ui/test/atoms/AgentDetailsPage.test.tsx b/packages/trueforge-ui/test/atoms/AgentDetailsPage.test.tsx
index 8d7f31bd8..31b0931b7 100644
--- a/packages/trueforge-ui/test/atoms/AgentDetailsPage.test.tsx
+++ b/packages/trueforge-ui/test/atoms/AgentDetailsPage.test.tsx
@@ -79,6 +79,7 @@ beforeAll(() => {
const detail: AgentDetail = {
agentId: 'agent-1',
name: 'release-notes-writer',
+ description: 'Produces concise release notes from merged PRs.',
agentSpec: {
model: { name: 'openai/gpt-5.1', params: { maxTokens: 16000 } },
instructions: '# Who you are\n\nWrite concise release notes.',
@@ -193,6 +194,7 @@ describe('AgentDetailsPage', () => {
const { getAgent } = renderPage();
expect(await screen.findByText('release-notes-writer')).toBeInTheDocument();
+ expect(await screen.findByText('Produces concise release notes from merged PRs.')).toBeInTheDocument();
expect(await screen.findByText('Write concise release notes.')).toBeInTheDocument();
expect(await screen.findByText('github')).toBeInTheDocument();
expect(await screen.findByText('release-writing')).toBeInTheDocument();
diff --git a/packages/trueforge-ui/test/atoms/AgentsLibrary.test.tsx b/packages/trueforge-ui/test/atoms/AgentsLibrary.test.tsx
index b9e338061..d4a604a66 100644
--- a/packages/trueforge-ui/test/atoms/AgentsLibrary.test.tsx
+++ b/packages/trueforge-ui/test/atoms/AgentsLibrary.test.tsx
@@ -35,9 +35,9 @@ function mockServer(
agents: Array<{
name: string;
agentId: string;
+ description?: string;
agentSpec?: {
model: { name: string };
- description?: string;
skills?: Array<{ id: string; name: string }>;
mcpServers?: Array<{ id: string; name: string }>;
};
@@ -146,7 +146,12 @@ describe('AgentsLibrary', () => {
it('lists agents and selects a named agent (Try = immutable)', async () => {
const server = mockServer([
- { name: 'alpha-agent', agentId: 'alpha-agent' },
+ {
+ name: 'alpha-agent',
+ agentId: 'alpha-agent',
+ description: 'Alpha handles triage.',
+ agentSpec: { model: { name: 'openai/gpt-4.1' } },
+ },
{ name: 'beta-agent', agentId: 'beta-agent' },
]);
const onSelectAgent = vi.fn();
@@ -158,6 +163,7 @@ describe('AgentsLibrary', () => {
await waitFor(() => {
expect(screen.getByRole('button', { name: 'Try agent alpha-agent' })).toBeInTheDocument();
});
+ expect(screen.getByText('Alpha handles triage.')).toHaveClass('truncate');
fireEvent.click(screen.getByRole('button', { name: 'Try agent beta-agent' }));
expect(onSelectAgent).toHaveBeenCalledWith('beta-agent');
@@ -166,6 +172,34 @@ describe('AgentsLibrary', () => {
});
});
+ it('truncates long descriptions without hiding Try, and skips name-echo descriptions', async () => {
+ const longDescription = `${'Lorem ipsum dolor sit amet, '.repeat(20)}consectetur.`;
+ const server = mockServer([
+ {
+ name: 'verbose-agent',
+ agentId: 'verbose-agent',
+ description: longDescription,
+ agentSpec: { model: { name: 'openai/gpt-4.1' } },
+ },
+ {
+ name: 'echo-agent',
+ agentId: 'echo-agent',
+ description: 'echo-agent',
+ agentSpec: { model: { name: 'openai/gpt-4.1' } },
+ },
+ ]);
+
+ renderLibrary(, { server });
+ fireEvent.click(screen.getByRole('button', { name: 'Open library' }));
+
+ await waitFor(() => {
+ expect(screen.getByRole('button', { name: 'Try agent verbose-agent' })).toBeInTheDocument();
+ });
+ expect(screen.getByRole('columnheader', { name: 'Configuration' })).toBeInTheDocument();
+ expect(screen.getByText(longDescription)).toHaveClass('truncate');
+ expect(screen.queryByText('echo-agent', { selector: '.text-xs' })).not.toBeInTheDocument();
+ });
+
it('shows Edit/Clone/Delete when composer is enabled and agentSpec is present', async () => {
const server = mockServer([
{
@@ -240,6 +274,7 @@ describe('AgentsLibrary', () => {
{
name: 'writer',
agentId: 'writer-id',
+ description: 'Writes release notes.',
agentSpec: { model: { name: 'openai-main/gpt-4.1' } },
},
]),
@@ -263,6 +298,7 @@ describe('AgentsLibrary', () => {
await waitFor(() => {
expect(saveAgent).toHaveBeenCalledWith({
agentName: 'writer-copy',
+ description: 'Writes release notes.',
agentSpec: { model: { name: 'openai-main/gpt-4.1' } },
intent: 'create',
});
diff --git a/packages/trueforge-ui/test/atoms/SaveAgentButton.test.tsx b/packages/trueforge-ui/test/atoms/SaveAgentButton.test.tsx
index 720d032fa..6b64f4112 100644
--- a/packages/trueforge-ui/test/atoms/SaveAgentButton.test.tsx
+++ b/packages/trueforge-ui/test/atoms/SaveAgentButton.test.tsx
@@ -102,7 +102,15 @@ function renderButton({
};
}
-function BoundMutableSaveButton({ agentId, agentName }: { agentId: string; agentName?: string }) {
+function BoundMutableSaveButton({
+ agentId,
+ agentName,
+ description,
+}: {
+ agentId: string;
+ agentName?: string;
+ description?: string;
+}) {
const { selectLibraryAgent } = useShellMode();
useEffect(() => {
selectLibraryAgent({
@@ -110,9 +118,10 @@ function BoundMutableSaveButton({ agentId, agentName }: { agentId: string; agent
isCreateAgent: true,
agentId,
agentName,
+ description,
agentSpec,
});
- }, [agentId, agentName, selectLibraryAgent]);
+ }, [agentId, agentName, description, selectLibraryAgent]);
return ;
}
@@ -205,7 +214,7 @@ describe('SaveAgentButton', () => {
'text-text-primary',
'focus-visible:ring-focus-ring/40',
);
- expect(within(dialog).queryByLabelText('Description')).not.toBeInTheDocument();
+ expect(within(dialog).getByLabelText('Description')).toBeInTheDocument();
expect(within(dialog).queryByRole('button', { name: 'Edit Model' })).not.toBeInTheDocument();
expect(within(dialog).queryByRole('button', { name: 'Edit Runtime Config' })).not.toBeInTheDocument();
expect(within(dialog).queryByRole('button', { name: 'Edit Connectors' })).not.toBeInTheDocument();
@@ -219,6 +228,7 @@ describe('SaveAgentButton', () => {
const dialog = await screen.findByRole('dialog', { name: 'Save agent' });
fireEvent.change(within(dialog).getByLabelText('Agent name'), { target: { value: 'writer' } });
+ fireEvent.change(within(dialog).getByLabelText('Description'), { target: { value: 'Writes docs.' } });
fireEvent.click(within(dialog).getByRole('button', { name: 'Save changes' }));
await waitFor(() =>
@@ -255,6 +265,7 @@ describe('SaveAgentButton', () => {
const dialog = await screen.findByRole('dialog', { name: 'Save agent' });
fireEvent.change(within(dialog).getByLabelText('Agent name'), { target: { value: 'latest-agent' } });
+ fireEvent.change(within(dialog).getByLabelText('Description'), { target: { value: 'Latest agent description.' } });
fireEvent.click(within(dialog).getByRole('button', { name: 'Save changes' }));
await waitFor(() =>
@@ -281,6 +292,7 @@ describe('SaveAgentButton', () => {
fireEvent.click(screen.getByRole('button', { name: 'Save Agent' }));
const dialog = await screen.findByRole('dialog', { name: 'Save agent' });
fireEvent.change(within(dialog).getByLabelText('Agent name'), { target: { value: 'writer' } });
+ fireEvent.change(within(dialog).getByLabelText('Description'), { target: { value: 'Writes docs.' } });
fireEvent.click(within(dialog).getByRole('button', { name: 'Save changes' }));
await waitFor(() =>
@@ -307,6 +319,7 @@ describe('SaveAgentButton', () => {
fireEvent.click(screen.getByRole('button', { name: 'Save Agent' }));
const dialog = await screen.findByRole('dialog', { name: 'Save agent' });
fireEvent.change(within(dialog).getByLabelText('Agent name'), { target: { value: 'writer' } });
+ fireEvent.change(within(dialog).getByLabelText('Description'), { target: { value: 'Writes docs.' } });
fireEvent.click(within(dialog).getByRole('button', { name: 'Save changes' }));
expect(updateAgentSpec).toHaveBeenCalledWith({
@@ -325,7 +338,7 @@ describe('SaveAgentButton', () => {
const saveAgent = vi.fn(async (): Promise => ({ agentId: 'writer' }));
renderButton({
saveAgent,
- children: ,
+ children: ,
});
const trigger = await screen.findByRole('button', { name: 'Update Agent' });
@@ -361,11 +374,13 @@ describe('SaveAgentButton', () => {
fireEvent.click(screen.getByRole('button', { name: 'Save Agent' }));
const dialog = await screen.findByRole('dialog', { name: 'Save agent' });
fireEvent.change(within(dialog).getByLabelText('Agent name'), { target: { value: 'my-agent' } });
+ fireEvent.change(within(dialog).getByLabelText('Description'), { target: { value: 'My agent description.' } });
fireEvent.click(within(dialog).getByRole('button', { name: 'Save changes' }));
await waitFor(() => expect(saveAgent).toHaveBeenCalledOnce());
expect(saveAgent).toHaveBeenCalledWith({
agentName: 'my-agent',
+ description: 'My agent description.',
agentSpec: {
model: { name: 'openai/gpt-4.1', params: undefined },
instructions: 'Be helpful.',
@@ -395,6 +410,9 @@ describe('SaveAgentButton', () => {
fireEvent.click(screen.getByRole('button', { name: 'Save Agent' }));
const saveDialog = await screen.findByRole('dialog', { name: 'Save agent' });
fireEvent.change(within(saveDialog).getByLabelText('Agent name'), { target: { value: 'preserved-agent' } });
+ fireEvent.change(within(saveDialog).getByLabelText('Description'), {
+ target: { value: 'Preserved agent description.' },
+ });
fireEvent.click(within(saveDialog).getByRole('button', { name: 'Save changes' }));
await waitFor(() => expect(saveAgent).toHaveBeenCalledOnce());
@@ -414,6 +432,7 @@ describe('SaveAgentButton', () => {
fireEvent.click(screen.getByRole('button', { name: 'Save Agent' }));
const dialog = await screen.findByRole('dialog', { name: 'Save agent' });
fireEvent.change(within(dialog).getByLabelText('Agent name'), { target: { value: 'pending-agent' } });
+ fireEvent.change(within(dialog).getByLabelText('Description'), { target: { value: 'Pending agent description.' } });
fireEvent.click(within(dialog).getByRole('button', { name: 'Save changes' }));
expect(within(dialog).getByLabelText('Agent name')).toBeDisabled();
@@ -432,6 +451,9 @@ describe('SaveAgentButton', () => {
fireEvent.click(screen.getByRole('button', { name: 'Save Agent' }));
const dialog = await screen.findByRole('dialog', { name: 'Save agent' });
fireEvent.change(within(dialog).getByLabelText('Agent name'), { target: { value: 'duplicate' } });
+ fireEvent.change(within(dialog).getByLabelText('Description'), {
+ target: { value: 'Duplicate agent description.' },
+ });
fireEvent.click(within(dialog).getByRole('button', { name: 'Save changes' }));
expect(await within(dialog).findByRole('alert')).toHaveTextContent('Agent name already exists');
@@ -452,10 +474,50 @@ describe('SaveAgentButton', () => {
fireEvent.click(screen.getByRole('button', { name: 'Save Agent' }));
const dialog = await screen.findByRole('dialog', { name: 'Save agent' });
fireEvent.change(within(dialog).getByLabelText('Agent name'), { target: { value: 'bad name' } });
+ fireEvent.change(within(dialog).getByLabelText('Description'), {
+ target: { value: 'Bad name agent description.' },
+ });
fireEvent.click(within(dialog).getByRole('button', { name: 'Save changes' }));
const alert = await within(dialog).findByRole('alert');
expect(alert).toHaveClass('whitespace-pre-wrap');
expect(alert.textContent).toBe('line one\nline two\tindented');
});
+
+ it('disables Save on create when description is empty', async () => {
+ renderButton();
+ fireEvent.click(screen.getByRole('button', { name: 'Save Agent' }));
+ const dialog = await screen.findByRole('dialog', { name: 'Save agent' });
+ fireEvent.change(within(dialog).getByLabelText('Agent name'), { target: { value: 'writer' } });
+ expect(within(dialog).getByRole('button', { name: 'Save changes' })).toBeDisabled();
+ fireEvent.change(within(dialog).getByLabelText('Description'), { target: { value: 'Writes docs.' } });
+ expect(within(dialog).getByRole('button', { name: 'Save changes' })).toBeEnabled();
+ });
+
+ it('disables Save on update when description is empty', async () => {
+ renderButton({
+ children: ,
+ });
+ fireEvent.click(await screen.findByRole('button', { name: 'Update Agent' }));
+ const dialog = await screen.findByRole('dialog', { name: 'Update agent' });
+ expect(within(dialog).getByRole('button', { name: 'Save changes' })).toBeEnabled();
+ fireEvent.change(within(dialog).getByLabelText('Description'), { target: { value: ' ' } });
+ expect(within(dialog).getByRole('button', { name: 'Save changes' })).toBeDisabled();
+ fireEvent.change(within(dialog).getByLabelText('Description'), { target: { value: 'Updated summary.' } });
+ expect(within(dialog).getByRole('button', { name: 'Save changes' })).toBeEnabled();
+ });
+
+ it('prefills description on update intent', async () => {
+ agentSpec = {
+ model: { name: 'openai/gpt-4.1' },
+ instructions: 'Be helpful.',
+ };
+ renderButton({
+ children: ,
+ });
+ fireEvent.click(await screen.findByRole('button', { name: 'Update Agent' }));
+ const dialog = await screen.findByRole('dialog', { name: 'Update agent' });
+ expect(within(dialog).getByLabelText('Agent name')).toHaveValue('writer');
+ expect(within(dialog).getByLabelText('Description')).toHaveValue('Existing agent summary.');
+ });
});
diff --git a/packages/trueforge-ui/test/containers/TrueForgeUI.test.tsx b/packages/trueforge-ui/test/containers/TrueForgeUI.test.tsx
index f635c8ca5..b6c1380f1 100644
--- a/packages/trueforge-ui/test/containers/TrueForgeUI.test.tsx
+++ b/packages/trueforge-ui/test/containers/TrueForgeUI.test.tsx
@@ -300,7 +300,7 @@ describe('TrueForgeUI', () => {
fireEvent.click(await screen.findByRole('button', { name: 'Save Agent' }));
const saveDialog = await screen.findByRole('dialog', { name: 'Save agent' });
expect(within(saveDialog).getByLabelText('Agent name')).toBeInTheDocument();
- expect(within(saveDialog).queryByLabelText('Description')).not.toBeInTheDocument();
+ expect(within(saveDialog).getByLabelText('Description')).toBeInTheDocument();
expect(within(saveDialog).queryByRole('button', { name: 'Edit Model' })).not.toBeInTheDocument();
expect(within(saveDialog).queryByRole('button', { name: 'Edit Connectors' })).not.toBeInTheDocument();
expect(getModels).toHaveBeenCalled();
diff --git a/packages/trueforge-ui/test/plugins/trueforge-agent-server-adapter/agentSessionsServer.test.ts b/packages/trueforge-ui/test/plugins/trueforge-agent-server-adapter/agentSessionsServer.test.ts
index 8a9b72f48..3e8059b9b 100644
--- a/packages/trueforge-ui/test/plugins/trueforge-agent-server-adapter/agentSessionsServer.test.ts
+++ b/packages/trueforge-ui/test/plugins/trueforge-agent-server-adapter/agentSessionsServer.test.ts
@@ -11,6 +11,7 @@ describe('createHarnessAgentSessionsServer', () => {
data: {
id: 'agent-1',
name: 'writer',
+ description: 'Writes docs.',
manifest: { model: { name: 'openai/gpt-5' }, instructions: 'Write.' },
},
}));
@@ -37,7 +38,11 @@ describe('createHarnessAgentSessionsServer', () => {
assert.deepEqual(await server.getAgent({ agentId: 'agent-1' }), {
agentId: 'agent-1',
name: 'writer',
- agentSpec: { model: { name: 'openai/gpt-5' }, instructions: 'Write.' },
+ description: 'Writes docs.',
+ agentSpec: {
+ model: { name: 'openai/gpt-5' },
+ instructions: 'Write.',
+ },
});
assert.deepEqual(await server.getCodeSnippets({ agentId: 'agent-1' }), [
{
diff --git a/packages/trueforge-ui/test/plugins/trueforge-agent-server-adapter/harnessBuilderServer.test.ts b/packages/trueforge-ui/test/plugins/trueforge-agent-server-adapter/harnessBuilderServer.test.ts
index 39875b156..a762004e2 100644
--- a/packages/trueforge-ui/test/plugins/trueforge-agent-server-adapter/harnessBuilderServer.test.ts
+++ b/packages/trueforge-ui/test/plugins/trueforge-agent-server-adapter/harnessBuilderServer.test.ts
@@ -305,11 +305,19 @@ describe('harnessBuilderServer', () => {
return Response.json({
data:
agentName === 'write'
- ? [{ id: 'agt_2', name: 'writer', manifest: { model: { name: 'test/model' } } }]
+ ? [
+ {
+ id: 'agt_2',
+ name: 'writer',
+ description: 'Writes docs.',
+ manifest: { model: { name: 'test/model' } },
+ },
+ ]
: [
{
id: 'agt_1',
name: 'reviewer',
+ description: 'Reviews pull requests.',
created_by_subject: {
subject_id: 'user-1',
subject_type: 'user',
@@ -322,7 +330,12 @@ describe('harnessBuilderServer', () => {
mcp_servers: [{ name: 'github', enable_tools: ['@all'] }],
},
},
- { id: 'agt_2', name: 'writer', manifest: { model: { name: 'test/model' } } },
+ {
+ id: 'agt_2',
+ name: 'writer',
+ description: 'Writes docs.',
+ manifest: { model: { name: 'test/model' } },
+ },
],
pagination: { limit: 25 },
});
@@ -341,6 +354,7 @@ describe('harnessBuilderServer', () => {
subjectType: 'user',
subjectDisplayName: 'Alice',
},
+ description: 'Reviews pull requests.',
agentSpec: {
model: { name: 'test/model' },
instructions: 'Review carefully.',
@@ -440,6 +454,47 @@ describe('harnessBuilderServer', () => {
assert.deepEqual(result, { agentId: 'agt_new' });
assert.deepEqual(requests.at(-1)?.body, {
name: 'saved-agent',
+ description: 'saved-agent',
+ manifest: {
+ model: { name: 'test/model' },
+ skills: [{ name: 'review', preload: false }],
+ },
+ });
+ });
+
+ it('saveAgent create sends top-level description and strips it from the manifest', async () => {
+ const requests: { method: string; url: string; body?: unknown }[] = [];
+ const fetchMock: typeof fetch = async (input, init) => {
+ const url = input instanceof Request ? input.url : String(input);
+ const method = init?.method ?? 'GET';
+ if (url.endsWith('/api/v1/agents') && method === 'POST' && typeof init?.body === 'string') {
+ requests.push({ method, url, body: JSON.parse(init.body) });
+ return Response.json({
+ data: {
+ id: 'agt_desc',
+ name: 'described-agent',
+ description: 'Helps with reviews.',
+ manifest: { model: { name: 'test/model' } },
+ },
+ });
+ }
+ return new Response(`Unexpected request: ${method} ${url}`, { status: 500 });
+ };
+
+ const builder = createHarnessBuilderServer({ fetch: fetchMock });
+ await builder.saveAgent({
+ agentName: 'described-agent',
+ description: 'Helps with reviews.',
+ agentSpec: {
+ model: { name: 'test/model' },
+ skills: [{ name: 'review' }],
+ },
+ intent: 'create',
+ });
+
+ assert.deepEqual(requests.at(-1)?.body, {
+ name: 'described-agent',
+ description: 'Helps with reviews.',
manifest: {
model: { name: 'test/model' },
skills: [{ name: 'review', preload: false }],
diff --git a/packages/trueforge/src/apis/agentImport.ts b/packages/trueforge/src/apis/agentImport.ts
index 24e144f75..a2cb00f8c 100644
--- a/packages/trueforge/src/apis/agentImport.ts
+++ b/packages/trueforge/src/apis/agentImport.ts
@@ -46,6 +46,7 @@ export function createAgentImportRouter(deps: AgentImportRouterDeps) {
const created = await agentStore.createAgent({
tenant_id: agent.tenant_id,
name: agent.name,
+ description: agent.name,
manifest: agent.manifest,
external_id: null,
created_by_subject: agent.created_by_subject,
diff --git a/packages/trueforge/src/apis/agents.ts b/packages/trueforge/src/apis/agents.ts
index 836ac021b..fafe32daf 100644
--- a/packages/trueforge/src/apis/agents.ts
+++ b/packages/trueforge/src/apis/agents.ts
@@ -48,6 +48,7 @@ function toWireAgent(record: AgentRecord): Agent {
return {
id: record.id,
name: record.name,
+ description: record.description,
manifest: record.manifest,
created_by_subject: record.created_by_subject,
};
@@ -117,6 +118,7 @@ export function createAgentsRouter(deps: AgentsRouterDeps(deps: AgentsRouterDeps {
getAgent(input: GetAgentInput, transaction?: TTransaction): Promise;
/** Inserts a new agent with a generated ULID. Throws AgentNameConflictError or AgentExternalIdConflictError on unique clash. */
createAgent(input: CreateAgentInput, transaction?: TTransaction): Promise;
- /** Patches `manifest` and/or `external_id`. Throws AgentExternalIdConflictError on unique clash. Returns undefined if missing. */
+ /** Patches `manifest`, `description`, and/or `external_id`. Throws AgentExternalIdConflictError on unique clash. Returns undefined if missing. */
updateAgent(input: UpdateAgentInput, transaction?: TTransaction): Promise;
/** Deletes by immutable id. Idempotent if already missing. */
deleteAgent(input: DeleteAgentInput, transaction?: TTransaction): Promise;
diff --git a/packages/trueforge/src/db/postgres/agent-store/PostgresAgentStore.ts b/packages/trueforge/src/db/postgres/agent-store/PostgresAgentStore.ts
index 02faf8ff9..5a647a839 100644
--- a/packages/trueforge/src/db/postgres/agent-store/PostgresAgentStore.ts
+++ b/packages/trueforge/src/db/postgres/agent-store/PostgresAgentStore.ts
@@ -30,6 +30,7 @@ function toRecord(row: Selectable): AgentRecord {
id: row.id,
tenant_id: row.tenant_id,
name: row.name,
+ description: row.description,
manifest: parseStoredAgentSpec(row.manifest),
external_id: row.external_id,
created_by_subject: CreatedBySubjectSchema.parse(row.created_by_subject),
@@ -147,6 +148,7 @@ export class PostgresAgentStore implements IAgentStore> {
id: newId(),
tenant_id: input.tenant_id,
name: input.name,
+ description: input.description,
manifest: json(input.manifest),
external_id: input.external_id,
created_by_subject: json(input.created_by_subject),
@@ -170,8 +172,8 @@ export class PostgresAgentStore implements IAgentStore> {
}
async updateAgent(input: UpdateAgentInput, transaction?: Transaction): Promise {
- if (input.manifest === undefined && input.external_id === undefined) {
- throw new Error('updateAgent requires manifest and/or external_id');
+ if (input.manifest === undefined && input.description === undefined && input.external_id === undefined) {
+ throw new Error('updateAgent requires manifest, description, and/or external_id');
}
const db = transaction ?? this.#db;
try {
@@ -179,6 +181,7 @@ export class PostgresAgentStore implements IAgentStore> {
.updateTable('agent')
.set({
...(input.manifest === undefined ? {} : { manifest: json(input.manifest) }),
+ ...(input.description === undefined ? {} : { description: input.description }),
...(input.external_id === undefined ? {} : { external_id: input.external_id }),
updated_at: now(),
})
diff --git a/packages/trueforge/src/db/postgres/migrations/20260909_000001_agent_description.ts b/packages/trueforge/src/db/postgres/migrations/20260909_000001_agent_description.ts
new file mode 100644
index 000000000..9e3bd1f4a
--- /dev/null
+++ b/packages/trueforge/src/db/postgres/migrations/20260909_000001_agent_description.ts
@@ -0,0 +1,16 @@
+import { sql, type Kysely } from 'kysely';
+
+/** Add agent `description`; backfill existing rows from `name`. */
+export async function up(db: Kysely): Promise {
+ await sql`SET LOCAL lock_timeout = '5s'`.execute(db);
+ await db.schema
+ .alterTable('agent')
+ .addColumn('description', 'text', col => col.notNull().defaultTo(''))
+ .execute();
+ await sql`UPDATE agent SET description = name`.execute(db);
+}
+
+export async function down(db: Kysely): Promise {
+ await sql`SET LOCAL lock_timeout = '5s'`.execute(db);
+ await db.schema.alterTable('agent').dropColumn('description').execute();
+}
diff --git a/packages/trueforge/src/db/postgres/types.ts b/packages/trueforge/src/db/postgres/types.ts
index 357ea5877..cdc6bcd64 100644
--- a/packages/trueforge/src/db/postgres/types.ts
+++ b/packages/trueforge/src/db/postgres/types.ts
@@ -383,6 +383,7 @@ export interface AgentTable {
tenant_id: string;
/** immutable natural uniqueness target within a tenant */
name: string;
+ description: string;
/** AgentSpec document; replaced whole on every upsert */
manifest: JSONColumnType;
external_id: string | null;
diff --git a/packages/trueforge/src/db/sqlite/agent-store/SqliteAgentStore.ts b/packages/trueforge/src/db/sqlite/agent-store/SqliteAgentStore.ts
index bfbad492a..27b1daa6a 100644
--- a/packages/trueforge/src/db/sqlite/agent-store/SqliteAgentStore.ts
+++ b/packages/trueforge/src/db/sqlite/agent-store/SqliteAgentStore.ts
@@ -35,6 +35,7 @@ function recordColumns(eb: ExpressionBuilder) {
'id' as const,
'tenant_id' as const,
'name' as const,
+ 'description' as const,
jsonText(eb.ref('manifest')).as('manifest'),
'external_id' as const,
jsonText(eb.ref('created_by_subject')).as('created_by_subject'),
@@ -47,6 +48,7 @@ function toRecord(row: {
id: string;
tenant_id: string;
name: AgentRecord['name'];
+ description: string;
manifest: AgentSpec;
external_id: string | null;
created_by_subject: CreatedBySubject;
@@ -152,6 +154,7 @@ export class SqliteAgentStore implements IAgentStore> {
id: newId(),
tenant_id: input.tenant_id,
name: input.name,
+ description: input.description,
manifest: jsonbBind(input.manifest),
external_id: input.external_id,
created_by_subject: jsonbBind(input.created_by_subject),
@@ -176,8 +179,8 @@ export class SqliteAgentStore implements IAgentStore> {
}
async updateAgent(input: UpdateAgentInput, transaction?: Transaction): Promise {
- if (input.manifest === undefined && input.external_id === undefined) {
- throw new Error('updateAgent requires manifest and/or external_id');
+ if (input.manifest === undefined && input.description === undefined && input.external_id === undefined) {
+ throw new Error('updateAgent requires manifest, description, and/or external_id');
}
const db = transaction ?? this.#db;
try {
@@ -185,6 +188,7 @@ export class SqliteAgentStore implements IAgentStore> {
.updateTable('agent')
.set({
...(input.manifest === undefined ? {} : { manifest: jsonbBind(input.manifest) }),
+ ...(input.description === undefined ? {} : { description: input.description }),
...(input.external_id === undefined ? {} : { external_id: input.external_id }),
updated_at: nowIso(),
})
diff --git a/packages/trueforge/src/db/sqlite/migrations/20260911_000001_agent_description.ts b/packages/trueforge/src/db/sqlite/migrations/20260911_000001_agent_description.ts
new file mode 100644
index 000000000..98cd85860
--- /dev/null
+++ b/packages/trueforge/src/db/sqlite/migrations/20260911_000001_agent_description.ts
@@ -0,0 +1,15 @@
+import { type Kysely, sql } from 'kysely';
+
+/** Add agent `description`; backfill existing rows from `name`. Mirrors postgres. */
+export async function up(db: Kysely): Promise {
+ await db.transaction().execute(async trx => {
+ await sql`ALTER TABLE agent ADD COLUMN description TEXT NOT NULL DEFAULT ''`.execute(trx);
+ await sql`UPDATE agent SET description = name`.execute(trx);
+ });
+}
+
+export async function down(db: Kysely): Promise {
+ await db.transaction().execute(async trx => {
+ await sql`ALTER TABLE agent DROP COLUMN description`.execute(trx);
+ });
+}
diff --git a/packages/trueforge/src/db/sqlite/types.ts b/packages/trueforge/src/db/sqlite/types.ts
index 40cd28aa1..c4340877b 100644
--- a/packages/trueforge/src/db/sqlite/types.ts
+++ b/packages/trueforge/src/db/sqlite/types.ts
@@ -227,6 +227,7 @@ export interface AgentTable {
tenant_id: string;
/** natural uniqueness target within a tenant */
name: string;
+ description: string;
/** AgentSpec document; replaced whole on every upsert */
manifest: JsonbColumn;
external_id: string | null;
diff --git a/packages/trueforge/src/routes/agentRoutes.ts b/packages/trueforge/src/routes/agentRoutes.ts
index cac16e525..aa1e88db7 100644
--- a/packages/trueforge/src/routes/agentRoutes.ts
+++ b/packages/trueforge/src/routes/agentRoutes.ts
@@ -181,7 +181,7 @@ export const putAgentRoute = createRoute({
path: '/{agent_id}',
tags: [OpenApiTag.AGENTS],
summary: 'Update an agent',
- description: 'Replaces the manifest for an existing agent keyed by immutable `agent_id`.',
+ description: 'Update an existing agent by immutable id.',
'x-fern-sdk-group-name': ['agents'],
'x-fern-sdk-method-name': 'update',
request: {
diff --git a/packages/trueforge/src/schemas/agent.ts b/packages/trueforge/src/schemas/agent.ts
index 122e65fa4..4a77b7f4f 100644
--- a/packages/trueforge/src/schemas/agent.ts
+++ b/packages/trueforge/src/schemas/agent.ts
@@ -12,20 +12,26 @@ import { NameSchema } from './common';
const RESERVED_AGENT_NAMES = new Set(['tfg', 'trueforge']);
+export const AGENT_DESCRIPTION_MAX_LENGTH = 1024;
+
+export const AgentDescriptionSchema = z.string().trim().min(1).max(AGENT_DESCRIPTION_MAX_LENGTH);
+
/** Create body: unique immutable `name` plus manifest. `id` is never client-supplied. */
export const CreateAgentRequestSchema = z
.object({
name: NameSchema.refine(name => !RESERVED_AGENT_NAMES.has(name), {
message: 'Agent name is reserved, cannot be used',
}),
+ description: AgentDescriptionSchema,
manifest: AgentSpecSchema,
})
.strict()
.openapi('CreateAgentRequest');
-/** PUT body: full manifest replacement only. */
+/** PUT body: full manifest replacement; `description` optional. */
export const UpdateAgentRequestSchema = z
.object({
+ description: AgentDescriptionSchema.optional(),
manifest: AgentSpecSchema,
})
.strict()
@@ -36,12 +42,12 @@ export const AgentSchema = z
.object({
id: z.string().min(1).describe('Immutable server-generated agent identifier.'),
name: NameSchema,
+ description: AgentDescriptionSchema,
manifest: AgentSpecSchema,
created_by_subject: CreatedBySubjectSchema,
})
.strict()
.openapi('Agent');
-
export const GetAgentResponseSchema = z.object({ data: AgentSchema }).openapi('GetAgentResponse');
export const ListAgentsResponseSchema = z
.object({
diff --git a/packages/trueforge/src/truefoundry/TrueFoundryAgentStore.ts b/packages/trueforge/src/truefoundry/TrueFoundryAgentStore.ts
index 4c9bfe819..46cec6cde 100644
--- a/packages/trueforge/src/truefoundry/TrueFoundryAgentStore.ts
+++ b/packages/trueforge/src/truefoundry/TrueFoundryAgentStore.ts
@@ -15,6 +15,7 @@ import {
} from '../db/agentStore';
import { PostgresAgentStore } from '../db/postgres/agent-store/PostgresAgentStore';
import type { Database } from '../db/postgres/types';
+import { AGENT_DESCRIPTION_MAX_LENGTH } from '../schemas/agent';
import { callerAccessToken, type ResolveAccessToken } from './accessToken';
import {
TrueFoundryServiceFoundryServerClient,
@@ -27,14 +28,16 @@ function asError(value: unknown): Error {
function toPutRemoteAgentPayload({
name,
+ description,
manifest,
}: {
name: string;
+ description: string;
manifest: AgentSpec;
}): Omit {
return {
name,
- description: name,
+ description: (description || name).slice(0, AGENT_DESCRIPTION_MAX_LENGTH),
model: manifest.model.name,
mcp_servers: (manifest.mcp_servers ?? []).map(server => server.name),
};
@@ -55,11 +58,11 @@ function toPutRemoteAgentPayload({
* update(external_id) fails after put: delete remote (if put returned an id), then
* delete local; if cleanup also fails, AggregateError (primary + cleanup errors).
*
- * update (manifest)
- * Happy path: lock → load row → put remote (new manifest) → write local.
+ * update (manifest and/or description)
+ * Happy path: lock → load row → put remote (new values) → write local.
* Missing row: return undefined (no remote call).
* putRemote fails: leave local unchanged; rethrow.
- * local write fails after put: best-effort putRemote(old manifest); if restore fails,
+ * local write fails after put: best-effort putRemote(old values); if restore fails,
* AggregateError; if restore ok, rethrow the DB error (local still old, remote restored).
* external_id-only patches skip ServiceFoundry and go straight to the inner store.
*
@@ -134,7 +137,11 @@ export class TrueFoundryAgentStore implements IAgentStore>
try {
({ externalId } = await this.#client.putRemoteAgent({
accessToken: await this.#resolveAccessToken(),
- ...toPutRemoteAgentPayload({ name: input.name, manifest: input.manifest }),
+ ...toPutRemoteAgentPayload({
+ name: input.name,
+ description: input.description,
+ manifest: input.manifest,
+ }),
}));
const updated = await this.#inner.updateAgent(
{ tenant_id: input.tenant_id, id: created.id, external_id: externalId },
@@ -169,9 +176,8 @@ export class TrueFoundryAgentStore implements IAgentStore>
}
async updateAgent(input: UpdateAgentInput, transaction?: Transaction): Promise {
- const nextManifest = input.manifest;
- if (nextManifest === undefined) {
- // No manifest means only `external_id` changed; pass through to the inner store.
+ if (input.manifest === undefined && input.description === undefined) {
+ // No manifest/description means only `external_id` changed; pass through to the inner store.
return this.#inner.updateAgent(input, transaction);
}
@@ -181,9 +187,16 @@ export class TrueFoundryAgentStore implements IAgentStore>
return undefined;
}
+ const nextManifest = input.manifest ?? previous.manifest;
+ const nextDescription = input.description ?? previous.description;
+
const { externalId } = await this.#client.putRemoteAgent({
accessToken: await this.#resolveAccessToken(),
- ...toPutRemoteAgentPayload({ name: previous.name, manifest: nextManifest }),
+ ...toPutRemoteAgentPayload({
+ name: previous.name,
+ description: nextDescription,
+ manifest: nextManifest,
+ }),
});
try {
@@ -191,7 +204,8 @@ export class TrueFoundryAgentStore implements IAgentStore>
{
tenant_id: input.tenant_id,
id: input.id,
- manifest: nextManifest,
+ ...(input.manifest === undefined ? {} : { manifest: nextManifest }),
+ ...(input.description === undefined ? {} : { description: nextDescription }),
...(externalId === previous.external_id ? {} : { external_id: externalId }),
},
txn,
@@ -202,6 +216,7 @@ export class TrueFoundryAgentStore implements IAgentStore>
accessToken: await this.#resolveAccessToken(),
...toPutRemoteAgentPayload({
name: previous.name,
+ description: previous.description,
manifest: previous.manifest,
}),
});
diff --git a/packages/trueforge/tests/db/agentStoreContractSuite.ts b/packages/trueforge/tests/db/agentStoreContractSuite.ts
index e123b80f1..7393fdc1e 100644
--- a/packages/trueforge/tests/db/agentStoreContractSuite.ts
+++ b/packages/trueforge/tests/db/agentStoreContractSuite.ts
@@ -30,12 +30,14 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: 'Research helper.',
manifest: manifest(),
external_id: null,
});
expect(created.tenant_id).toBe(TENANT);
expect(created.name).toBe('research');
+ expect(created.description).toBe('Research helper.');
expect(created.id.length).toBeGreaterThan(0);
expect(created.manifest).toEqual(manifest());
expect(created.external_id).toBeNull();
@@ -62,6 +64,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: 'Research helper.',
manifest: manifest(),
external_id: null,
});
@@ -70,6 +73,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
const updated = await store.updateAgent({
tenant_id: TENANT,
id: created.id,
+ description: 'Updated research helper.',
manifest: replacement,
});
@@ -77,6 +81,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
expect.objectContaining({
id: created.id,
name: 'research',
+ description: 'Updated research helper.',
manifest: replacement,
created_at: created.created_at,
}),
@@ -107,6 +112,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -116,6 +122,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
}),
@@ -128,6 +135,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'zeta',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -135,6 +143,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'alpha',
+ description: 'Test agent.',
manifest: manifest({ instructions: 'Alpha agent.' }),
external_id: null,
});
@@ -142,6 +151,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: 'other-tenant',
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -162,6 +172,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'local-only',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -169,6 +180,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'linked',
+ description: 'Test agent.',
manifest: manifest(),
external_id: 'sf-agent-1',
});
@@ -176,6 +188,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'other-linked',
+ description: 'Test agent.',
manifest: manifest(),
external_id: 'sf-agent-2',
});
@@ -226,6 +239,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'alpha-bot',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -233,6 +247,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'bravo-bot',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -240,6 +255,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'unrelated',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -267,6 +283,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'charlie',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -274,6 +291,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'alpha',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -281,6 +299,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'bravo',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -312,6 +331,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -325,6 +345,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: 'Test agent.',
manifest: manifest(),
external_id: 'sf-agent-1',
});
@@ -338,6 +359,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'alpha',
+ description: 'Test agent.',
manifest: manifest(),
external_id: 'shared-key',
});
@@ -346,6 +368,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'beta',
+ description: 'Test agent.',
manifest: manifest(),
external_id: 'shared-key',
}),
@@ -354,6 +377,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: 'other-tenant',
created_by_subject: CREATED_BY_SUBJECT,
name: 'alpha',
+ description: 'Test agent.',
manifest: manifest(),
external_id: 'shared-key',
});
@@ -361,6 +385,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'gamma',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -368,6 +393,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'delta',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -379,6 +405,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
@@ -402,6 +429,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'alpha',
+ description: 'Test agent.',
manifest: manifest(),
external_id: 'shared-key',
});
@@ -409,6 +437,7 @@ export function runAgentStoreContractSuite(getStore: () => IAgentStore): void {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'beta',
+ description: 'Test agent.',
manifest: manifest(),
external_id: null,
});
diff --git a/packages/trueforge/tests/db/scheduleDispatchContractSuite.ts b/packages/trueforge/tests/db/scheduleDispatchContractSuite.ts
index 4d87c2df2..e7b922036 100644
--- a/packages/trueforge/tests/db/scheduleDispatchContractSuite.ts
+++ b/packages/trueforge/tests/db/scheduleDispatchContractSuite.ts
@@ -71,6 +71,7 @@ export function runScheduleDispatchContractSuite(deps: {
tenant_id: TENANT,
created_by_subject: USER_SUBJECT,
name: `agent-${String(Date.now())}-${String(seq)}`,
+ description: 'Test agent.',
manifest: AgentSpecSchema.parse({
model: { name: 'anthropic/claude-sonnet-4-6' },
instructions: 'Be helpful.',
diff --git a/packages/trueforge/tests/db/scheduleStoreContractSuite.ts b/packages/trueforge/tests/db/scheduleStoreContractSuite.ts
index 488fe53d3..3f2470daa 100644
--- a/packages/trueforge/tests/db/scheduleStoreContractSuite.ts
+++ b/packages/trueforge/tests/db/scheduleStoreContractSuite.ts
@@ -31,6 +31,7 @@ export function runScheduleStoreContractSuite(deps: {
tenant_id: TENANT,
created_by_subject: USER_SUBJECT,
name: `agent-${String(Date.now())}-${String(Math.random()).slice(2, 8)}`,
+ description: 'Test agent.',
manifest: AgentSpecSchema.parse({
model: { name: 'anthropic/claude-sonnet-4-6' },
instructions: 'Be helpful.',
diff --git a/packages/trueforge/tests/unit/apis/agents.test.ts b/packages/trueforge/tests/unit/apis/agents.test.ts
index edf2ca0f7..dcdc1dd5f 100644
--- a/packages/trueforge/tests/unit/apis/agents.test.ts
+++ b/packages/trueforge/tests/unit/apis/agents.test.ts
@@ -39,10 +39,12 @@ const manifest = {
const writeBody = {
name: 'research',
+ description: 'Research helper.',
manifest,
};
const updateBody = {
+ description: 'Updated research agent.',
manifest: {
model: { name: 'anthropic/claude-sonnet-4-6' },
instructions: 'Updated instructions.',
@@ -52,6 +54,7 @@ const updateBody = {
type WireAgent = {
id: string;
name: string;
+ description: string;
manifest: {
model: { name: string };
instructions?: string;
@@ -115,12 +118,13 @@ describe('agents router', () => {
});
it('POST returns a wrapped Agent; PUT by immutable id keeps the same id', async () => {
- const created = await router.request('/', jsonInit('POST', writeBody));
+ const created = await router.request('/', jsonInit('POST', { ...writeBody, description: ' Research helper. ' }));
expect(created.status).toBe(201);
const createdJson = (await created.json()) as { data: WireAgent };
expect(createdJson.data.id.length).toBeGreaterThan(0);
expect(createdJson.data).toMatchObject({
name: 'research',
+ description: 'Research helper.',
manifest: {
model: { name: 'anthropic/claude-sonnet-4-6' },
instructions: 'Be helpful.',
@@ -141,10 +145,26 @@ describe('agents router', () => {
const updatedJson = (await updated.json()) as { data: WireAgent };
expect(updatedJson.data.id).toBe(createdJson.data.id);
expect(updatedJson.data.name).toBe('research');
+ expect(updatedJson.data.description).toBe('Updated research agent.');
expect(updatedJson.data.manifest.instructions).toBe('Updated instructions.');
expect(updatedJson.data).not.toHaveProperty('metadata');
});
+ it('PUT with only manifest keeps the stored description', async () => {
+ const created = await router.request(
+ '/',
+ jsonInit('POST', { ...writeBody, name: 'keep-desc', description: 'Keep me.' }),
+ );
+ expect(created.status).toBe(201);
+ const createdJson = (await created.json()) as { data: WireAgent };
+
+ const updated = await router.request(`/${createdJson.data.id}`, jsonInit('PUT', { manifest: updateBody.manifest }));
+ expect(updated.status).toBe(200);
+ const updatedJson = (await updated.json()) as { data: WireAgent };
+ expect(updatedJson.data.description).toBe('Keep me.');
+ expect(updatedJson.data.manifest.instructions).toBe('Updated instructions.');
+ });
+
it('PUT rejects metadata in the request body', async () => {
const created = await router.request('/', jsonInit('POST', { ...writeBody, name: 'no-meta' }));
expect(created.status).toBe(201);
@@ -223,11 +243,21 @@ describe('agents router', () => {
'/',
jsonInit('POST', {
name: 'other',
+ description: 'Other agent.',
manifest: { ...manifest, model: { name: 'missing/model' } },
}),
);
expect(unknownModel.status).toBe(422);
+ const blankDescription = await router.request(
+ '/',
+ jsonInit('POST', { ...writeBody, name: 'blank-desc', description: ' ' }),
+ );
+ expect(blankDescription.status).toBe(400);
+
+ const missingDescription = await router.request('/', jsonInit('POST', { name: 'no-desc', manifest }));
+ expect(missingDescription.status).toBe(400);
+
const first = await router.request('/', jsonInit('POST', { ...writeBody, name: 'alpha' }));
expect(first.status).toBe(201);
diff --git a/packages/trueforge/tests/unit/apis/schedules.test.ts b/packages/trueforge/tests/unit/apis/schedules.test.ts
index a9da14a3e..7be02cf23 100644
--- a/packages/trueforge/tests/unit/apis/schedules.test.ts
+++ b/packages/trueforge/tests/unit/apis/schedules.test.ts
@@ -88,6 +88,7 @@ async function setup(authorizer: Authorizer = new TrueForgeAuthorizer()) {
subject_display_name: 'alice',
},
name: 'reporter',
+ description: 'Test agent.',
manifest: AgentSpecSchema.parse({ model: { name: 'test-provider/test-model' }, instructions: 'test' }),
external_id: 'reporter-external-id',
});
@@ -247,6 +248,7 @@ describe('schedule list agent_names filter', () => {
subject_display_name: 'alice',
},
name: 'reporter-two',
+ description: 'Test agent.',
manifest: AgentSpecSchema.parse({ model: { name: 'test-provider/test-model' }, instructions: 'test' }),
external_id: null,
});
@@ -436,6 +438,7 @@ describe('internal schedule execution', () => {
subject_display_name: 'alice',
},
name: 'reporter',
+ description: 'reporter description',
manifest: AgentSpecSchema.parse({ model: { name: 'test-provider/test-model' }, instructions: 'test' }),
external_id: 'reporter-external-id',
});
diff --git a/packages/trueforge/tests/unit/apis/sessionHttp.test.ts b/packages/trueforge/tests/unit/apis/sessionHttp.test.ts
index f4b51c7aa..2112b6fd1 100644
--- a/packages/trueforge/tests/unit/apis/sessionHttp.test.ts
+++ b/packages/trueforge/tests/unit/apis/sessionHttp.test.ts
@@ -153,6 +153,7 @@ describe('sessions HTTP agent binding', () => {
subject_display_name: STANDALONE_REQUEST_CONTEXT.subject.display_name,
},
name: 'named-agent',
+ description: 'Test agent.',
manifest: AgentSpecSchema.parse({
model: { name: 'anthropic/claude-sonnet-4-6' },
instructions: 'from-registry',
@@ -185,6 +186,7 @@ describe('sessions HTTP agent binding', () => {
subject_display_name: STANDALONE_REQUEST_CONTEXT.subject.display_name,
},
name: 'metrics-agent',
+ description: 'Test agent.',
manifest: inlineSpec,
external_id: null,
});
@@ -244,6 +246,7 @@ describe('sessions HTTP agent binding', () => {
tenant_id: 'default',
created_by_subject: { subject_id: 'owner', subject_type: 'user', subject_display_name: 'Owner' },
name: 'managed-agent',
+ description: 'Test agent.',
manifest: inlineSpec,
external_id: 'managed-agent-external',
});
@@ -424,6 +427,7 @@ describe('sessions HTTP agent binding', () => {
subject_display_name: STANDALONE_REQUEST_CONTEXT.subject.display_name,
},
name: 'named-agent',
+ description: 'Test agent.',
manifest: AgentSpecSchema.parse({
model: { name: 'anthropic/claude-sonnet-4-6' },
instructions: 'from-registry',
@@ -605,6 +609,7 @@ describe('sessions HTTP agent binding', () => {
subject_display_name: STANDALONE_REQUEST_CONTEXT.subject.display_name,
},
name: 'forbidden-agent',
+ description: 'Test agent.',
manifest: inlineSpec,
external_id: null,
});
diff --git a/packages/trueforge/tests/unit/apis/turns.test.ts b/packages/trueforge/tests/unit/apis/turns.test.ts
index 426fbece4..42c872ad9 100644
--- a/packages/trueforge/tests/unit/apis/turns.test.ts
+++ b/packages/trueforge/tests/unit/apis/turns.test.ts
@@ -124,6 +124,7 @@ describe('turns', () => {
const agent = await agentStore.createAgent({
tenant_id: 'default',
name: 'managed-agent',
+ description: 'Test agent.',
manifest: AgentSpecSchema.parse({ model: { name: 'test-provider/test-model' } }),
external_id: 'managed-agent-external',
created_by_subject: { subject_id: 'owner', subject_type: 'user', subject_display_name: 'Owner' },
@@ -433,6 +434,7 @@ describe('turns', () => {
subject_display_name: STANDALONE_REQUEST_CONTEXT.subject.display_name,
},
name: 'named-for-turn',
+ description: 'Test agent.',
manifest: AgentSpecSchema.parse({
model: { name: 'test-provider/test-model' },
instructions: 'test',
diff --git a/packages/trueforge/tests/unit/truefoundry/TrueFoundryAgentStore.test.ts b/packages/trueforge/tests/unit/truefoundry/TrueFoundryAgentStore.test.ts
index 12c1b1e97..9d66285fe 100644
--- a/packages/trueforge/tests/unit/truefoundry/TrueFoundryAgentStore.test.ts
+++ b/packages/trueforge/tests/unit/truefoundry/TrueFoundryAgentStore.test.ts
@@ -59,6 +59,7 @@ function record(overrides: Partial = {}): AgentRecord {
id: 'agent-1',
tenant_id: TENANT,
name: 'research',
+ description: 'research',
manifest: manifest(),
external_id: null,
created_by_subject: CREATED_BY_SUBJECT,
@@ -183,6 +184,7 @@ describe('TrueFoundryAgentStore', () => {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: 'research',
manifest: manifest({ mcp_servers: [{ name: 'slack' }] }),
external_id: null,
},
@@ -220,6 +222,7 @@ describe('TrueFoundryAgentStore', () => {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: 'research',
manifest: manifest(),
external_id: null,
},
@@ -231,7 +234,7 @@ describe('TrueFoundryAgentStore', () => {
expect(deleteRemoteAgent).not.toHaveBeenCalled();
});
- it('createAgent uses agent name as description even when instructions are empty', async () => {
+ it('createAgent uses agent name as description when description is blank', async () => {
const putRemoteAgent = jest.fn(async (input: PutRemoteAgentInput) => {
expect(input.description).toBe('research');
expect(input.mcp_servers).toEqual([]);
@@ -249,6 +252,7 @@ describe('TrueFoundryAgentStore', () => {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: '',
manifest: AgentSpecSchema.parse({ model: { name: 'openai-gateway/gpt-5' }, instructions: '' }),
external_id: null,
},
@@ -257,6 +261,32 @@ describe('TrueFoundryAgentStore', () => {
expect(putRemoteAgent).toHaveBeenCalled();
});
+ it('createAgent syncs description to ServiceFoundry', async () => {
+ const putRemoteAgent = jest.fn(async (input: PutRemoteAgentInput) => {
+ expect(input.description).toBe('Finds papers');
+ return { externalId: 'sf-1' };
+ });
+ const createAgent = jest.fn(async () => record({ external_id: null, description: 'Finds papers' }));
+ const updateAgent = jest.fn(async () => record({ external_id: 'sf-1', description: 'Finds papers' }));
+ const store = tfStore({
+ inner: mockInner({ createAgent, updateAgent }),
+ client: mockClient({ putRemoteAgent }),
+ });
+
+ await store.createAgent(
+ {
+ tenant_id: TENANT,
+ created_by_subject: CREATED_BY_SUBJECT,
+ name: 'research',
+ description: 'Finds papers',
+ manifest: manifest(),
+ external_id: null,
+ },
+ TXN,
+ );
+ expect(putRemoteAgent).toHaveBeenCalled();
+ });
+
it('createAgent deletes the local row when putRemoteAgent fails', async () => {
const local = record({ external_id: null });
const createAgent = jest.fn(async () => local);
@@ -277,6 +307,7 @@ describe('TrueFoundryAgentStore', () => {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: 'research',
manifest: manifest(),
external_id: null,
},
@@ -307,6 +338,7 @@ describe('TrueFoundryAgentStore', () => {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: 'research',
manifest: manifest(),
external_id: null,
},
@@ -343,6 +375,7 @@ describe('TrueFoundryAgentStore', () => {
tenant_id: TENANT,
created_by_subject: CREATED_BY_SUBJECT,
name: 'research',
+ description: 'research',
manifest: manifest(),
external_id: null,
},
diff --git a/packages/trueforge/tests/unit/truefoundry/TrueFoundryMcpServerStore.test.ts b/packages/trueforge/tests/unit/truefoundry/TrueFoundryMcpServerStore.test.ts
index 95c7c9586..2913b3870 100644
--- a/packages/trueforge/tests/unit/truefoundry/TrueFoundryMcpServerStore.test.ts
+++ b/packages/trueforge/tests/unit/truefoundry/TrueFoundryMcpServerStore.test.ts
@@ -20,6 +20,7 @@ const AGENT: AgentRecord = {
id: 'agent-1',
tenant_id: TENANT,
name: 'named',
+ description: 'Test agent.',
manifest: AgentSpecSchema.parse({ model: { name: 'p/m' } }),
external_id: 'ext-agent',
created_by_subject: { subject_id: 'user-1', subject_type: 'user', subject_display_name: 'User' },
diff --git a/packages/trueforge/tests/unit/truefoundry/TrueFoundryModelProviderStore.test.ts b/packages/trueforge/tests/unit/truefoundry/TrueFoundryModelProviderStore.test.ts
index 652c42056..454c77ff0 100644
--- a/packages/trueforge/tests/unit/truefoundry/TrueFoundryModelProviderStore.test.ts
+++ b/packages/trueforge/tests/unit/truefoundry/TrueFoundryModelProviderStore.test.ts
@@ -13,6 +13,7 @@ const AGENT: AgentRecord = {
id: 'agent-1',
tenant_id: TENANT,
name: 'named',
+ description: 'Test agent.',
manifest: AgentSpecSchema.parse({ model: { name: 'p/m' } }),
external_id: 'ext-agent',
created_by_subject: { subject_id: 'user-1', subject_type: 'user', subject_display_name: 'User' },
diff --git a/packages/trueforge/tests/unit/truefoundry/accessToken.test.ts b/packages/trueforge/tests/unit/truefoundry/accessToken.test.ts
index bddd67fc3..99b5143db 100644
--- a/packages/trueforge/tests/unit/truefoundry/accessToken.test.ts
+++ b/packages/trueforge/tests/unit/truefoundry/accessToken.test.ts
@@ -24,6 +24,7 @@ const AGENT: AgentRecord = {
id: 'agent-1',
tenant_id: 'acme',
name: 'named',
+ description: 'Test agent.',
manifest: AgentSpecSchema.parse({ model: { name: 'p/m' } }),
external_id: 'ext-agent',
created_by_subject: { subject_id: 'user-1', subject_type: 'user', subject_display_name: 'User' },
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d91ce6711..be90d6ee2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -69,8 +69,8 @@ importers:
specifier: 0.2.21
version: 0.2.21(@assistant-ui/tap@0.9.5(@types/react@19.2.18)(react@19.2.8))(@types/react@19.2.18)(react@19.2.8)
'@truefoundry/assistant-ui-runtime':
- specifier: 0.1.36
- version: 0.1.36(@assistant-ui/tap@0.9.5(@types/react@19.2.18)(react@19.2.8))(@types/react@19.2.18)(assistant-cloud@0.1.41(redis@5.12.1(@opentelemetry/api@1.9.1)))(react@19.2.8)(redis@5.12.1(@opentelemetry/api@1.9.1))(truefoundry-gateway-sdk@0.4.2)(zustand@5.0.15(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))
+ specifier: 0.1.37
+ version: 0.1.37(@assistant-ui/tap@0.9.5(@types/react@19.2.18)(react@19.2.8))(@types/react@19.2.18)(assistant-cloud@0.1.41(redis@5.12.1(@opentelemetry/api@1.9.1)))(react@19.2.8)(redis@5.12.1(@opentelemetry/api@1.9.1))(truefoundry-gateway-sdk@0.4.2)(zustand@5.0.15(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))
'@truefoundry/trueforge-sdk':
specifier: workspace:*
version: link:../trueforge-sdk
@@ -366,8 +366,8 @@ importers:
specifier: ^0.13.2
version: 0.13.3(@openuidev/react-headless@0.9.4(react@19.2.8)(zustand@5.0.15(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8))))(@openuidev/react-lang@0.2.9(@modelcontextprotocol/sdk@1.30.0(supports-color@8.1.1)(zod@4.4.3))(react@19.2.8)(zod@4.4.3))(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@8.1.1)(zod@4.4.3)(zustand@5.0.15(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))
'@truefoundry/assistant-ui-runtime':
- specifier: 0.1.36
- version: 0.1.36(@assistant-ui/tap@0.9.5(@types/react@19.2.18)(react@19.2.8))(@types/react@19.2.18)(assistant-cloud@0.1.41(redis@5.12.1(@opentelemetry/api@1.9.1)))(react@19.2.8)(redis@5.12.1(@opentelemetry/api@1.9.1))(truefoundry-gateway-sdk@0.4.2)(zustand@5.0.15(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))
+ specifier: 0.1.37
+ version: 0.1.37(@assistant-ui/tap@0.9.5(@types/react@19.2.18)(react@19.2.8))(@types/react@19.2.18)(assistant-cloud@0.1.41(redis@5.12.1(@opentelemetry/api@1.9.1)))(react@19.2.8)(redis@5.12.1(@opentelemetry/api@1.9.1))(truefoundry-gateway-sdk@0.4.2)(zustand@5.0.15(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))
'@truefoundry/trueforge-sdk':
specifier: workspace:*
version: link:../trueforge-sdk
@@ -1077,12 +1077,6 @@ packages:
cpu: [ppc64]
os: [aix]
- '@esbuild/aix-ppc64@0.28.1':
- resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [aix]
-
'@esbuild/aix-ppc64@0.28.2':
resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==}
engines: {node: '>=18'}
@@ -1101,12 +1095,6 @@ packages:
cpu: [arm64]
os: [android]
- '@esbuild/android-arm64@0.28.1':
- resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [android]
-
'@esbuild/android-arm64@0.28.2':
resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==}
engines: {node: '>=18'}
@@ -1125,12 +1113,6 @@ packages:
cpu: [arm]
os: [android]
- '@esbuild/android-arm@0.28.1':
- resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [android]
-
'@esbuild/android-arm@0.28.2':
resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==}
engines: {node: '>=18'}
@@ -1149,12 +1131,6 @@ packages:
cpu: [x64]
os: [android]
- '@esbuild/android-x64@0.28.1':
- resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [android]
-
'@esbuild/android-x64@0.28.2':
resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==}
engines: {node: '>=18'}
@@ -1173,12 +1149,6 @@ packages:
cpu: [arm64]
os: [darwin]
- '@esbuild/darwin-arm64@0.28.1':
- resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [darwin]
-
'@esbuild/darwin-arm64@0.28.2':
resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==}
engines: {node: '>=18'}
@@ -1197,12 +1167,6 @@ packages:
cpu: [x64]
os: [darwin]
- '@esbuild/darwin-x64@0.28.1':
- resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [darwin]
-
'@esbuild/darwin-x64@0.28.2':
resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==}
engines: {node: '>=18'}
@@ -1221,12 +1185,6 @@ packages:
cpu: [arm64]
os: [freebsd]
- '@esbuild/freebsd-arm64@0.28.1':
- resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [freebsd]
-
'@esbuild/freebsd-arm64@0.28.2':
resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==}
engines: {node: '>=18'}
@@ -1245,12 +1203,6 @@ packages:
cpu: [x64]
os: [freebsd]
- '@esbuild/freebsd-x64@0.28.1':
- resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [freebsd]
-
'@esbuild/freebsd-x64@0.28.2':
resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==}
engines: {node: '>=18'}
@@ -1269,12 +1221,6 @@ packages:
cpu: [arm64]
os: [linux]
- '@esbuild/linux-arm64@0.28.1':
- resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [linux]
-
'@esbuild/linux-arm64@0.28.2':
resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==}
engines: {node: '>=18'}
@@ -1293,12 +1239,6 @@ packages:
cpu: [arm]
os: [linux]
- '@esbuild/linux-arm@0.28.1':
- resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [linux]
-
'@esbuild/linux-arm@0.28.2':
resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==}
engines: {node: '>=18'}
@@ -1317,12 +1257,6 @@ packages:
cpu: [ia32]
os: [linux]
- '@esbuild/linux-ia32@0.28.1':
- resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [linux]
-
'@esbuild/linux-ia32@0.28.2':
resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==}
engines: {node: '>=18'}
@@ -1341,12 +1275,6 @@ packages:
cpu: [loong64]
os: [linux]
- '@esbuild/linux-loong64@0.28.1':
- resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==}
- engines: {node: '>=18'}
- cpu: [loong64]
- os: [linux]
-
'@esbuild/linux-loong64@0.28.2':
resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==}
engines: {node: '>=18'}
@@ -1365,12 +1293,6 @@ packages:
cpu: [mips64el]
os: [linux]
- '@esbuild/linux-mips64el@0.28.1':
- resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==}
- engines: {node: '>=18'}
- cpu: [mips64el]
- os: [linux]
-
'@esbuild/linux-mips64el@0.28.2':
resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==}
engines: {node: '>=18'}
@@ -1389,12 +1311,6 @@ packages:
cpu: [ppc64]
os: [linux]
- '@esbuild/linux-ppc64@0.28.1':
- resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [linux]
-
'@esbuild/linux-ppc64@0.28.2':
resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==}
engines: {node: '>=18'}
@@ -1413,12 +1329,6 @@ packages:
cpu: [riscv64]
os: [linux]
- '@esbuild/linux-riscv64@0.28.1':
- resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==}
- engines: {node: '>=18'}
- cpu: [riscv64]
- os: [linux]
-
'@esbuild/linux-riscv64@0.28.2':
resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==}
engines: {node: '>=18'}
@@ -1437,12 +1347,6 @@ packages:
cpu: [s390x]
os: [linux]
- '@esbuild/linux-s390x@0.28.1':
- resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==}
- engines: {node: '>=18'}
- cpu: [s390x]
- os: [linux]
-
'@esbuild/linux-s390x@0.28.2':
resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==}
engines: {node: '>=18'}
@@ -1461,12 +1365,6 @@ packages:
cpu: [x64]
os: [linux]
- '@esbuild/linux-x64@0.28.1':
- resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [linux]
-
'@esbuild/linux-x64@0.28.2':
resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==}
engines: {node: '>=18'}
@@ -1485,12 +1383,6 @@ packages:
cpu: [arm64]
os: [netbsd]
- '@esbuild/netbsd-arm64@0.28.1':
- resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [netbsd]
-
'@esbuild/netbsd-arm64@0.28.2':
resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==}
engines: {node: '>=18'}
@@ -1509,12 +1401,6 @@ packages:
cpu: [x64]
os: [netbsd]
- '@esbuild/netbsd-x64@0.28.1':
- resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [netbsd]
-
'@esbuild/netbsd-x64@0.28.2':
resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==}
engines: {node: '>=18'}
@@ -1533,12 +1419,6 @@ packages:
cpu: [arm64]
os: [openbsd]
- '@esbuild/openbsd-arm64@0.28.1':
- resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [openbsd]
-
'@esbuild/openbsd-arm64@0.28.2':
resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==}
engines: {node: '>=18'}
@@ -1557,12 +1437,6 @@ packages:
cpu: [x64]
os: [openbsd]
- '@esbuild/openbsd-x64@0.28.1':
- resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [openbsd]
-
'@esbuild/openbsd-x64@0.28.2':
resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==}
engines: {node: '>=18'}
@@ -1581,12 +1455,6 @@ packages:
cpu: [arm64]
os: [openharmony]
- '@esbuild/openharmony-arm64@0.28.1':
- resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [openharmony]
-
'@esbuild/openharmony-arm64@0.28.2':
resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==}
engines: {node: '>=18'}
@@ -1605,12 +1473,6 @@ packages:
cpu: [x64]
os: [sunos]
- '@esbuild/sunos-x64@0.28.1':
- resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [sunos]
-
'@esbuild/sunos-x64@0.28.2':
resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==}
engines: {node: '>=18'}
@@ -1629,12 +1491,6 @@ packages:
cpu: [arm64]
os: [win32]
- '@esbuild/win32-arm64@0.28.1':
- resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [win32]
-
'@esbuild/win32-arm64@0.28.2':
resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==}
engines: {node: '>=18'}
@@ -1653,12 +1509,6 @@ packages:
cpu: [ia32]
os: [win32]
- '@esbuild/win32-ia32@0.28.1':
- resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [win32]
-
'@esbuild/win32-ia32@0.28.2':
resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==}
engines: {node: '>=18'}
@@ -1677,12 +1527,6 @@ packages:
cpu: [x64]
os: [win32]
- '@esbuild/win32-x64@0.28.1':
- resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [win32]
-
'@esbuild/win32-x64@0.28.2':
resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==}
engines: {node: '>=18'}
@@ -3648,8 +3492,8 @@ packages:
'@types/react-dom':
optional: true
- '@truefoundry/assistant-ui-runtime@0.1.36':
- resolution: {integrity: sha512-bjI3lRfvmTwy44hKHlf/RuJN8eLqIpm6ph9rjjBKXw/5D1fGWdGJCB/TDzeSDAHPGij63yJkFCvYXEbMdDx8Kg==}
+ '@truefoundry/assistant-ui-runtime@0.1.37':
+ resolution: {integrity: sha512-nFWitRLb6umJofzjjd/1+XhL7QzJ77tw+hTyriCEdb+9E7R8Z4qwM8ZMmmkC4b7P7P18LqgGh+kqYinaqsTRQg==}
peerDependencies:
'@types/react': '*'
react: ^18 || ^19
@@ -4828,11 +4672,6 @@ packages:
engines: {node: '>=18'}
hasBin: true
- esbuild@0.28.1:
- resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
- engines: {node: '>=18'}
- hasBin: true
-
esbuild@0.28.2:
resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==}
engines: {node: '>=18'}
@@ -8497,9 +8336,6 @@ snapshots:
'@esbuild/aix-ppc64@0.27.7':
optional: true
- '@esbuild/aix-ppc64@0.28.1':
- optional: true
-
'@esbuild/aix-ppc64@0.28.2':
optional: true
@@ -8509,9 +8345,6 @@ snapshots:
'@esbuild/android-arm64@0.27.7':
optional: true
- '@esbuild/android-arm64@0.28.1':
- optional: true
-
'@esbuild/android-arm64@0.28.2':
optional: true
@@ -8521,9 +8354,6 @@ snapshots:
'@esbuild/android-arm@0.27.7':
optional: true
- '@esbuild/android-arm@0.28.1':
- optional: true
-
'@esbuild/android-arm@0.28.2':
optional: true
@@ -8533,9 +8363,6 @@ snapshots:
'@esbuild/android-x64@0.27.7':
optional: true
- '@esbuild/android-x64@0.28.1':
- optional: true
-
'@esbuild/android-x64@0.28.2':
optional: true
@@ -8545,9 +8372,6 @@ snapshots:
'@esbuild/darwin-arm64@0.27.7':
optional: true
- '@esbuild/darwin-arm64@0.28.1':
- optional: true
-
'@esbuild/darwin-arm64@0.28.2':
optional: true
@@ -8557,9 +8381,6 @@ snapshots:
'@esbuild/darwin-x64@0.27.7':
optional: true
- '@esbuild/darwin-x64@0.28.1':
- optional: true
-
'@esbuild/darwin-x64@0.28.2':
optional: true
@@ -8569,9 +8390,6 @@ snapshots:
'@esbuild/freebsd-arm64@0.27.7':
optional: true
- '@esbuild/freebsd-arm64@0.28.1':
- optional: true
-
'@esbuild/freebsd-arm64@0.28.2':
optional: true
@@ -8581,9 +8399,6 @@ snapshots:
'@esbuild/freebsd-x64@0.27.7':
optional: true
- '@esbuild/freebsd-x64@0.28.1':
- optional: true
-
'@esbuild/freebsd-x64@0.28.2':
optional: true
@@ -8593,9 +8408,6 @@ snapshots:
'@esbuild/linux-arm64@0.27.7':
optional: true
- '@esbuild/linux-arm64@0.28.1':
- optional: true
-
'@esbuild/linux-arm64@0.28.2':
optional: true
@@ -8605,9 +8417,6 @@ snapshots:
'@esbuild/linux-arm@0.27.7':
optional: true
- '@esbuild/linux-arm@0.28.1':
- optional: true
-
'@esbuild/linux-arm@0.28.2':
optional: true
@@ -8617,9 +8426,6 @@ snapshots:
'@esbuild/linux-ia32@0.27.7':
optional: true
- '@esbuild/linux-ia32@0.28.1':
- optional: true
-
'@esbuild/linux-ia32@0.28.2':
optional: true
@@ -8629,9 +8435,6 @@ snapshots:
'@esbuild/linux-loong64@0.27.7':
optional: true
- '@esbuild/linux-loong64@0.28.1':
- optional: true
-
'@esbuild/linux-loong64@0.28.2':
optional: true
@@ -8641,9 +8444,6 @@ snapshots:
'@esbuild/linux-mips64el@0.27.7':
optional: true
- '@esbuild/linux-mips64el@0.28.1':
- optional: true
-
'@esbuild/linux-mips64el@0.28.2':
optional: true
@@ -8653,9 +8453,6 @@ snapshots:
'@esbuild/linux-ppc64@0.27.7':
optional: true
- '@esbuild/linux-ppc64@0.28.1':
- optional: true
-
'@esbuild/linux-ppc64@0.28.2':
optional: true
@@ -8665,9 +8462,6 @@ snapshots:
'@esbuild/linux-riscv64@0.27.7':
optional: true
- '@esbuild/linux-riscv64@0.28.1':
- optional: true
-
'@esbuild/linux-riscv64@0.28.2':
optional: true
@@ -8677,9 +8471,6 @@ snapshots:
'@esbuild/linux-s390x@0.27.7':
optional: true
- '@esbuild/linux-s390x@0.28.1':
- optional: true
-
'@esbuild/linux-s390x@0.28.2':
optional: true
@@ -8689,9 +8480,6 @@ snapshots:
'@esbuild/linux-x64@0.27.7':
optional: true
- '@esbuild/linux-x64@0.28.1':
- optional: true
-
'@esbuild/linux-x64@0.28.2':
optional: true
@@ -8701,9 +8489,6 @@ snapshots:
'@esbuild/netbsd-arm64@0.27.7':
optional: true
- '@esbuild/netbsd-arm64@0.28.1':
- optional: true
-
'@esbuild/netbsd-arm64@0.28.2':
optional: true
@@ -8713,9 +8498,6 @@ snapshots:
'@esbuild/netbsd-x64@0.27.7':
optional: true
- '@esbuild/netbsd-x64@0.28.1':
- optional: true
-
'@esbuild/netbsd-x64@0.28.2':
optional: true
@@ -8725,9 +8507,6 @@ snapshots:
'@esbuild/openbsd-arm64@0.27.7':
optional: true
- '@esbuild/openbsd-arm64@0.28.1':
- optional: true
-
'@esbuild/openbsd-arm64@0.28.2':
optional: true
@@ -8737,9 +8516,6 @@ snapshots:
'@esbuild/openbsd-x64@0.27.7':
optional: true
- '@esbuild/openbsd-x64@0.28.1':
- optional: true
-
'@esbuild/openbsd-x64@0.28.2':
optional: true
@@ -8749,9 +8525,6 @@ snapshots:
'@esbuild/openharmony-arm64@0.27.7':
optional: true
- '@esbuild/openharmony-arm64@0.28.1':
- optional: true
-
'@esbuild/openharmony-arm64@0.28.2':
optional: true
@@ -8761,9 +8534,6 @@ snapshots:
'@esbuild/sunos-x64@0.27.7':
optional: true
- '@esbuild/sunos-x64@0.28.1':
- optional: true
-
'@esbuild/sunos-x64@0.28.2':
optional: true
@@ -8773,9 +8543,6 @@ snapshots:
'@esbuild/win32-arm64@0.27.7':
optional: true
- '@esbuild/win32-arm64@0.28.1':
- optional: true
-
'@esbuild/win32-arm64@0.28.2':
optional: true
@@ -8785,9 +8552,6 @@ snapshots:
'@esbuild/win32-ia32@0.27.7':
optional: true
- '@esbuild/win32-ia32@0.28.1':
- optional: true
-
'@esbuild/win32-ia32@0.28.2':
optional: true
@@ -8797,9 +8561,6 @@ snapshots:
'@esbuild/win32-x64@0.27.7':
optional: true
- '@esbuild/win32-x64@0.28.1':
- optional: true
-
'@esbuild/win32-x64@0.28.2':
optional: true
@@ -10908,7 +10669,7 @@ snapshots:
'@types/react': 19.2.18
'@types/react-dom': 19.2.5(@types/react@19.2.18)
- '@truefoundry/assistant-ui-runtime@0.1.36(@assistant-ui/tap@0.9.5(@types/react@19.2.18)(react@19.2.8))(@types/react@19.2.18)(assistant-cloud@0.1.41(redis@5.12.1(@opentelemetry/api@1.9.1)))(react@19.2.8)(redis@5.12.1(@opentelemetry/api@1.9.1))(truefoundry-gateway-sdk@0.4.2)(zustand@5.0.15(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))':
+ '@truefoundry/assistant-ui-runtime@0.1.37(@assistant-ui/tap@0.9.5(@types/react@19.2.18)(react@19.2.8))(@types/react@19.2.18)(assistant-cloud@0.1.41(redis@5.12.1(@opentelemetry/api@1.9.1)))(react@19.2.8)(redis@5.12.1(@opentelemetry/api@1.9.1))(truefoundry-gateway-sdk@0.4.2)(zustand@5.0.15(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))':
dependencies:
'@assistant-ui/core': 0.2.22(@assistant-ui/store@0.2.21(@assistant-ui/tap@0.9.5(@types/react@19.2.18)(react@19.2.8))(@types/react@19.2.18)(react@19.2.8))(@assistant-ui/tap@0.9.5(@types/react@19.2.18)(react@19.2.8))(@types/react@19.2.18)(assistant-cloud@0.1.41(redis@5.12.1(@opentelemetry/api@1.9.1)))(react@19.2.8)(redis@5.12.1(@opentelemetry/api@1.9.1))(zustand@5.0.15(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))
'@assistant-ui/store': 0.2.21(@assistant-ui/tap@0.9.5(@types/react@19.2.18)(react@19.2.8))(@types/react@19.2.18)(react@19.2.8)
@@ -11098,10 +10859,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/eslint-plugin@8.68.0(@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)':
+ '@typescript-eslint/eslint-plugin@8.68.0(@typescript-eslint/parser@8.68.0(eslint@10.9.1(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.68.0(eslint@9.39.5(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.68.0(eslint@10.9.1(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.68.0
'@typescript-eslint/type-utils': 8.68.0(eslint@9.39.5(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)
'@typescript-eslint/utils': 8.68.0(eslint@9.39.5(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)
@@ -12169,35 +11930,6 @@ snapshots:
'@esbuild/win32-ia32': 0.27.7
'@esbuild/win32-x64': 0.27.7
- esbuild@0.28.1:
- optionalDependencies:
- '@esbuild/aix-ppc64': 0.28.1
- '@esbuild/android-arm': 0.28.1
- '@esbuild/android-arm64': 0.28.1
- '@esbuild/android-x64': 0.28.1
- '@esbuild/darwin-arm64': 0.28.1
- '@esbuild/darwin-x64': 0.28.1
- '@esbuild/freebsd-arm64': 0.28.1
- '@esbuild/freebsd-x64': 0.28.1
- '@esbuild/linux-arm': 0.28.1
- '@esbuild/linux-arm64': 0.28.1
- '@esbuild/linux-ia32': 0.28.1
- '@esbuild/linux-loong64': 0.28.1
- '@esbuild/linux-mips64el': 0.28.1
- '@esbuild/linux-ppc64': 0.28.1
- '@esbuild/linux-riscv64': 0.28.1
- '@esbuild/linux-s390x': 0.28.1
- '@esbuild/linux-x64': 0.28.1
- '@esbuild/netbsd-arm64': 0.28.1
- '@esbuild/netbsd-x64': 0.28.1
- '@esbuild/openbsd-arm64': 0.28.1
- '@esbuild/openbsd-x64': 0.28.1
- '@esbuild/openharmony-arm64': 0.28.1
- '@esbuild/sunos-x64': 0.28.1
- '@esbuild/win32-arm64': 0.28.1
- '@esbuild/win32-ia32': 0.28.1
- '@esbuild/win32-x64': 0.28.1
-
esbuild@0.28.2:
optionalDependencies:
'@esbuild/aix-ppc64': 0.28.2
@@ -12226,7 +11958,6 @@ snapshots:
'@esbuild/win32-arm64': 0.28.2
'@esbuild/win32-ia32': 0.28.2
'@esbuild/win32-x64': 0.28.2
- optional: true
escalade@3.2.0: {}
@@ -15304,7 +15035,7 @@ snapshots:
tsx@4.23.12:
dependencies:
- esbuild: 0.28.1
+ esbuild: 0.28.2
optionalDependencies:
fsevents: 2.3.3
@@ -15339,7 +15070,7 @@ snapshots:
typescript-eslint@8.68.0(eslint@9.39.5(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3):
dependencies:
- '@typescript-eslint/eslint-plugin': 8.68.0(@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)
+ '@typescript-eslint/eslint-plugin': 8.68.0(@typescript-eslint/parser@8.68.0(eslint@10.9.1(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)
'@typescript-eslint/parser': 8.68.0(eslint@9.39.5(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)
'@typescript-eslint/typescript-estree': 8.68.0(supports-color@8.1.1)(typescript@5.9.3)
'@typescript-eslint/utils': 8.68.0(eslint@9.39.5(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)