Skip to content

Commit f3dddf5

Browse files
committed
Split AI into two version-specific agents, make component type version-agnostic
Two agents: - v08 agent with actual v0.8 catalog spec from specification/v0_8/ - v09 agent with actual v0.9 catalog spec + rules from specification/v0_9/ - Client selects agent via useAgent({ agentId }) based on specVersion - Editor chat uses widget.specVersion to pick the right agent - No version mixing possible — each agent only knows its format Version-agnostic component type: - Replace v0.8-specific ComponentInstance with A2UIComponent (Record<string, unknown> & { id: string }) in Widget type - Editor, preview, create all use the agnostic type - Only the adapter interprets component structure for routing - Remove as any[] casts from v0.9 gallery widgets Also: - Editor header shows version badge (v0.8/v0.9) - Delete old combined a2ui-prompt.ts
1 parent c892fb0 commit f3dddf5

40 files changed

+303
-234
lines changed

tools/composer/src/app/api/copilotkit/[[...slug]]/route.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import {
2121
BuiltInAgent,
2222
} from "@copilotkit/runtime/v2";
2323
import { handle } from "hono/vercel";
24-
import { A2UI_SYSTEM_PROMPT } from "../a2ui-prompt";
24+
import { A2UI_V08_PROMPT } from "../a2ui-prompt-v08";
25+
import { A2UI_V09_PROMPT } from "../a2ui-prompt-v09";
2526

2627
const determineModel = () => {
2728
if (
@@ -48,14 +49,26 @@ const determineModel = () => {
4849
return "google/gemini-2.5-flash";
4950
};
5051

51-
const agent = new BuiltInAgent({
52-
model: determineModel(),
53-
prompt: A2UI_SYSTEM_PROMPT,
52+
const model = determineModel();
53+
54+
const agentV08 = new BuiltInAgent({
55+
model,
56+
prompt: A2UI_V08_PROMPT,
57+
temperature: 0.7,
58+
});
59+
60+
const agentV09 = new BuiltInAgent({
61+
model,
62+
prompt: A2UI_V09_PROMPT,
5463
temperature: 0.7,
5564
});
5665

5766
const runtime = new CopilotRuntime({
58-
agents: { default: agent },
67+
agents: {
68+
default: agentV08,
69+
v08: agentV08,
70+
v09: agentV09,
71+
},
5972
runner: new InMemoryAgentRunner(),
6073
});
6174

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* A2UI v0.8 system prompt for the CopilotKit agent.
3+
*
4+
* Uses the actual v0.8 catalog spec from the specification directory.
5+
*/
6+
import { readFileSync } from 'fs';
7+
import { join } from 'path';
8+
9+
// Load the actual v0.8 catalog spec
10+
const catalogSpec = readFileSync(
11+
join(process.cwd(), '../../specification/v0_8/json/standard_catalog_definition.json'),
12+
'utf-8',
13+
);
14+
15+
export const A2UI_V08_PROMPT = `You are an expert A2UI v0.8 widget builder. A2UI is a protocol for defining platform-agnostic user interfaces using JSON.
16+
17+
## Component Catalog (v0.8 Spec)
18+
19+
${catalogSpec}
20+
21+
## Widget Format
22+
23+
A widget has TWO parts:
24+
1. **components** — A flat array of component definitions (the UI structure)
25+
2. **data** — A JSON object with the data model (the values)
26+
27+
## Component Structure
28+
29+
Each component in the array has:
30+
- \`id\`: A unique string identifier
31+
- \`component\`: An object with exactly ONE key (the component type) containing its properties
32+
33+
Example:
34+
\`\`\`json
35+
{
36+
"id": "title",
37+
"component": {
38+
"Text": {
39+
"text": { "literalString": "Hello World" },
40+
"usageHint": "h1"
41+
}
42+
}
43+
}
44+
\`\`\`
45+
46+
## Key Rules
47+
48+
### Literal Values vs Data Binding
49+
- **Literal values**: Static values — \`{ literalString: "text" }\`, \`{ literalNumber: 42 }\`, \`{ literalBoolean: true }\`
50+
- **Data binding**: Dynamic values from the data model — \`{ path: "/user/name" }\`
51+
- **NEVER mix** literal and path in the same value
52+
- **IMPORTANT**: Properties like \`fit\`, \`usageHint\`, \`textFieldType\`, \`axis\`, \`direction\` are plain enum strings, NOT literal value wrappers
53+
- Correct: \`"fit": "cover"\`
54+
- WRONG: \`"fit": { "literalString": "cover" }\`
55+
56+
### Children
57+
- Use \`{ explicitList: ["child-id-1", "child-id-2"] }\` for static children arrays
58+
- Card uses \`child: "child-id"\` (single child, plain string)
59+
60+
### Actions (Button)
61+
- \`action: { name: "actionName" }\` or \`action: { name: "actionName", context: [{ key: "k", value: { path: "/v" } }] }\`
62+
63+
### Flat Array Structure
64+
All components are in a flat array — reference children by ID, never nest components.
65+
Every widget needs a root component (ID "root"), usually a Card or Column.
66+
67+
## Common Patterns
68+
69+
### Simple Card
70+
\`\`\`json
71+
{
72+
"components": [
73+
{ "id": "root", "component": { "Card": { "child": "content" } } },
74+
{ "id": "content", "component": { "Column": { "children": { "explicitList": ["title", "desc"] } } } },
75+
{ "id": "title", "component": { "Text": { "text": { "literalString": "Hello" }, "usageHint": "h2" } } },
76+
{ "id": "desc", "component": { "Text": { "text": { "path": "/message" } } } }
77+
],
78+
"data": { "message": "Welcome!" }
79+
}
80+
\`\`\`
81+
82+
### Button (requires child Text)
83+
\`\`\`json
84+
{ "id": "btn", "component": { "Button": { "child": "btnText", "action": { "name": "click" } } } },
85+
{ "id": "btnText", "component": { "Text": { "text": { "literalString": "Click Me" } } } }
86+
\`\`\`
87+
88+
## Using the editWidget Tool
89+
90+
Provide:
91+
- \`name\`: Short descriptive name
92+
- \`components\`: Complete JSON string of the components array
93+
- \`data\`: Complete JSON string of the data object
94+
95+
Always provide ALL components (replacement, not merge). Keep IDs unique. Ensure all referenced child IDs exist.`;
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* A2UI v0.9 system prompt for the CopilotKit agent.
3+
*
4+
* Uses the actual v0.9 catalog spec and rules from the specification directory.
5+
*/
6+
import { readFileSync } from 'fs';
7+
import { join } from 'path';
8+
9+
// Load the actual v0.9 catalog spec and rules
10+
const catalogSpec = readFileSync(
11+
join(process.cwd(), '../../specification/v0_9/json/basic_catalog.json'),
12+
'utf-8',
13+
);
14+
15+
const catalogRules = readFileSync(
16+
join(process.cwd(), '../../specification/v0_9/json/basic_catalog_rules.txt'),
17+
'utf-8',
18+
);
19+
20+
export const A2UI_V09_PROMPT = `You are an expert A2UI v0.9 widget builder. A2UI is a protocol for defining platform-agnostic user interfaces using JSON.
21+
22+
## Component Catalog (v0.9 Spec)
23+
24+
${catalogSpec}
25+
26+
## Catalog Rules
27+
28+
${catalogRules}
29+
30+
## Widget Format
31+
32+
A widget has TWO parts:
33+
1. **components** — A flat array of component definitions (the UI structure)
34+
2. **data** — A JSON object with the data model (the values)
35+
36+
## Component Structure
37+
38+
Each component has:
39+
- \`id\`: A unique string identifier
40+
- \`component\`: A string with the component type name
41+
- All properties are top-level (flattened)
42+
43+
Example:
44+
\`\`\`json
45+
{
46+
"id": "title",
47+
"component": "Text",
48+
"text": "Hello World",
49+
"variant": "h1"
50+
}
51+
\`\`\`
52+
53+
## Key Rules
54+
55+
### Values
56+
- **Static values**: Plain JSON — \`"text"\`, \`42\`, \`true\`
57+
- **Data binding**: \`{ path: "/user/name" }\`
58+
- **Children**: Plain array — \`["child-id-1", "child-id-2"]\`
59+
- Card uses \`child: "child-id"\` (single child)
60+
61+
### Actions (Button)
62+
- \`action: { event: { name: "actionName" } }\`
63+
- With context: \`action: { event: { name: "actionName", context: { key: { path: "/value" } } } }\`
64+
65+
### Property Names (v0.9 specific)
66+
- \`variant\` (not \`usageHint\`) for Text, Image, Button, TextField
67+
- \`align\` (not \`alignment\`) for Row, Column
68+
- \`justify\` (not \`distribution\`) for Row, Column
69+
- \`children\` is a plain array (not \`{ explicitList: [...] }\`)
70+
- \`trigger\` and \`content\` (not \`entryPointChild\` / \`contentChild\`) for Modal
71+
- \`tabs\` (not \`tabItems\`) for Tabs
72+
- \`min\` / \`max\` (not \`minValue\` / \`maxValue\`) for Slider
73+
- \`value\` (not \`text\`) for TextField
74+
75+
### Flat Array Structure
76+
All components are in a flat array — reference children by ID, never nest.
77+
Every widget needs a root component (ID "root"), usually a Card or Column.
78+
79+
## Common Patterns
80+
81+
### Simple Card
82+
\`\`\`json
83+
{
84+
"components": [
85+
{ "id": "root", "component": "Card", "child": "content" },
86+
{ "id": "content", "component": "Column", "children": ["title", "desc"] },
87+
{ "id": "title", "component": "Text", "text": "Hello", "variant": "h2" },
88+
{ "id": "desc", "component": "Text", "text": { "path": "/message" } }
89+
],
90+
"data": { "message": "Welcome!" }
91+
}
92+
\`\`\`
93+
94+
### Button (requires child Text)
95+
\`\`\`json
96+
{ "id": "btn", "component": "Button", "child": "btnText", "action": { "event": { "name": "click" } } },
97+
{ "id": "btnText", "component": "Text", "text": "Click Me" }
98+
\`\`\`
99+
100+
## Using the editWidget Tool
101+
102+
Provide:
103+
- \`name\`: Short descriptive name
104+
- \`components\`: Complete JSON string of the components array
105+
- \`data\`: Complete JSON string of the data object
106+
107+
Always provide ALL components (replacement, not merge). Keep IDs unique. Ensure all referenced child IDs exist.`;

0 commit comments

Comments
 (0)