Skip to content

Commit f772e86

Browse files
committed
Merge branch 'main' into claude/objective-cohen-fb1f7d
Three conflicts, all additive collisions in lists main also grew: - `ARCHITECTURE.md`: main added `thread_agent_kwargs`, `a2ui` and `url_fetch_policy` rows to the Python config table. Kept all of them and reinserted `template_tools_provider` after `session_manager_provider`. - `python/README.md`: main expanded the Key Files table with five more modules. Kept them and reinserted `template_tools.py` beside `client_proxy_tool.py`, the other module that syncs the tool registry. - `typescript/src/__tests__/exports.test.ts`: main appended `DEFAULT_URL_FETCH_POLICY` and `UrlFetchPolicyError` to the expected export list. Kept both alongside `syncTemplateTools` and `parkedBatchToolNames`. `error-codes.json` merged clean and stayed alphabetical: `TEMPLATE_TOOLS_PROVIDER_ERROR` sits before `THREAD_AGENT_CONFIG_ERROR`, and main's new `URL_FETCH_POLICY_INVALID` last. Both READMEs' new "Terminal error codes" sections point at that file and enumerate only the divergences, so a shared code carrying a byte-identical template on both sides needs no entry there. Nothing in main's token-usage or URL-fetch-policy work touches the request path this branch adds, and the per-request template-tools sync still sits between the interrupt-session gate and the proxy sync on both sides. Verified after the merge: 1356 passed / 1 skipped (Python), 1714 passed (TypeScript), 78 passed (TypeScript examples), `tsc --noEmit` clean.
2 parents 6dee0a7 + 4cbeb9b commit f772e86

29 files changed

Lines changed: 4916 additions & 282 deletions

apps/dojo/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"generate-content-json": "npx tsx scripts/generate-content-json.ts",
1212
"test:crewai-config": "tsx --test src/crewai.test.ts",
1313
"run-everything": "./scripts/prep-dojo-everything.js && ./scripts/run-dojo-everything.js",
14-
"local-install": "bash scripts/local-install.sh"
14+
"local-install": "bash scripts/local-install.sh",
15+
"test:a2ui-config": "tsx --test src/a2ui-config.test.ts"
1516
},
1617
"dependencies": {
1718
"@a2a-js/sdk": "0.2.5",
@@ -91,7 +92,8 @@
9192
"tailwindcss-animate": "^1.0.7",
9293
"untruncate-json": "^0.0.1",
9394
"uuid": "^11.1.0",
94-
"zod": "^3.25.75"
95+
"zod": "^3.25.75",
96+
"zod-to-json-schema": "3.25.2"
9597
},
9698
"peerDependencies": {
9799
"@ag-ui/client": "workspace:*",

apps/dojo/src/a2ui-config.test.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import assert from "node:assert/strict";
2+
import test from "node:test";
3+
4+
import {
5+
AbstractAgent,
6+
type BaseEvent,
7+
EventType,
8+
type RunAgentInput,
9+
} from "@ag-ui/client";
10+
import {
11+
A2UIMiddleware,
12+
A2UI_SCHEMA_CONTEXT_DESCRIPTION,
13+
} from "@ag-ui/a2ui-middleware";
14+
import { Observable, firstValueFrom, toArray } from "rxjs";
15+
16+
import { dynamicSchemaCatalog } from "./a2ui-catalog";
17+
import { DOJO_A2UI_MIDDLEWARE_CONFIG } from "./a2ui-config";
18+
19+
class CaptureAgent extends AbstractAgent {
20+
readonly runCalls: RunAgentInput[] = [];
21+
22+
run(input: RunAgentInput): Observable<BaseEvent> {
23+
this.runCalls.push(input);
24+
return new Observable((subscriber) => {
25+
subscriber.next({
26+
type: EventType.RUN_STARTED,
27+
threadId: input.threadId,
28+
runId: input.runId,
29+
});
30+
subscriber.next({
31+
type: EventType.RUN_FINISHED,
32+
threadId: input.threadId,
33+
runId: input.runId,
34+
});
35+
subscriber.complete();
36+
});
37+
}
38+
}
39+
40+
test("Dojo A2UI middleware forwards the rendered dynamic catalog", async () => {
41+
const downstream = new CaptureAgent();
42+
const middleware = new A2UIMiddleware(DOJO_A2UI_MIDDLEWARE_CONFIG);
43+
44+
await firstValueFrom(
45+
middleware
46+
.run(
47+
{
48+
threadId: "test-thread",
49+
runId: "test-run",
50+
tools: [],
51+
context: [],
52+
forwardedProps: {},
53+
state: {},
54+
messages: [],
55+
},
56+
downstream,
57+
)
58+
.pipe(toArray()),
59+
);
60+
61+
assert.equal(downstream.runCalls.length, 1);
62+
const forwarded = downstream.runCalls[0];
63+
const schemaContext = forwarded.context.find(
64+
({ description }) => description === A2UI_SCHEMA_CONTEXT_DESCRIPTION,
65+
);
66+
assert.ok(
67+
schemaContext && typeof schemaContext.value === "string",
68+
"dynamic catalog schema must reach downstream agent context",
69+
);
70+
71+
const schema = JSON.parse(schemaContext.value) as {
72+
catalogId: string;
73+
components: Record<
74+
string,
75+
{ allOf?: Array<{ properties?: Record<string, unknown> }> }
76+
>;
77+
};
78+
assert.equal(schema.catalogId, dynamicSchemaCatalog.id);
79+
assert.deepEqual(Object.keys(schema.components), [
80+
...dynamicSchemaCatalog.components.keys(),
81+
]);
82+
83+
const hotelProperties = schema.components.HotelCard.allOf?.find(
84+
({ properties }) => properties,
85+
)?.properties;
86+
assert.ok(hotelProperties);
87+
assert.ok("pricePerNight" in hotelProperties);
88+
const rating = hotelProperties.rating as {
89+
anyOf?: Array<{ type?: string }>;
90+
};
91+
assert.ok(rating.anyOf?.some(({ type }) => type === "number"));
92+
});

apps/dojo/src/a2ui-config.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { A2UIMiddlewareConfig } from "@ag-ui/a2ui-middleware";
2+
import { zodToJsonSchema } from "zod-to-json-schema";
3+
4+
import {
5+
HotelCardApi,
6+
ProductCardApi,
7+
RowApi,
8+
TeamMemberCardApi,
9+
} from "./a2ui-catalog/apis";
10+
11+
const DOJO_A2UI_CATALOG_ID = "https://a2ui.org/demos/dojo/dynamic_catalog.json";
12+
13+
const dynamicComponentApis = [
14+
RowApi,
15+
HotelCardApi,
16+
ProductCardApi,
17+
TeamMemberCardApi,
18+
];
19+
20+
const components = Object.fromEntries(
21+
dynamicComponentApis.map((componentApi) => {
22+
const schema = zodToJsonSchema(componentApi.schema, {
23+
target: "jsonSchema2019-09",
24+
}) as {
25+
properties?: Record<string, unknown>;
26+
required?: string[];
27+
};
28+
return [
29+
componentApi.name,
30+
{
31+
allOf: [
32+
{ $ref: "common_types.json#/$defs/ComponentCommon" },
33+
{
34+
properties: {
35+
component: { const: componentApi.name },
36+
...(schema.properties ?? {}),
37+
},
38+
required: ["component", ...(schema.required ?? [])],
39+
},
40+
],
41+
},
42+
];
43+
}),
44+
);
45+
46+
export const DOJO_A2UI_MIDDLEWARE_CONFIG = {
47+
injectA2UITool: true,
48+
defaultCatalogId: DOJO_A2UI_CATALOG_ID,
49+
schema: {
50+
catalogId: DOJO_A2UI_CATALOG_ID,
51+
components,
52+
},
53+
} satisfies A2UIMiddlewareConfig;

apps/dojo/src/agents.ts

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,14 @@ import { Ag2Agent } from "@ag-ui/ag2";
4444
import { LangroidHttpAgent } from "@ag-ui/langroid";
4545
import { WatsonxAgent } from "@ag-ui/watsonx";
4646
import { A2UIMiddleware } from "@ag-ui/a2ui-middleware";
47+
import { DOJO_A2UI_MIDDLEWARE_CONFIG } from "./a2ui-config";
4748
import {
4849
CREWAI_CONVERSATIONAL_AGENT_PATHS,
4950
CREWAI_FLOW_AGENT_PATHS,
5051
} from "./crewai";
5152

5253
const envVars = getEnvVars();
5354

54-
// Catalog the dojo's dynamic A2UI demos render against (HotelCard / ProductCard
55-
// / TeamMemberCard / Row).
56-
const A2UI_DOJO_CATALOG_ID = "https://a2ui.org/demos/dojo/dynamic_catalog.json";
57-
5855
// Per-agent A2UI inject whitelist for the adk-middleware integration. These
5956
// subagent demos wire no a2ui tool themselves and rely on the adapter
6057
// auto-injecting `generate_a2ui` when it sees `injectA2UITool`. Injection is
@@ -101,10 +98,7 @@ function createCrewAIIntegrationAgents<const T extends Record<string, string>>(
10198
);
10299
for (const id of CREWAI_A2UI_INJECT_AGENTS) {
103100
(agents as Record<string, AbstractAgent>)[id]?.use(
104-
new A2UIMiddleware({
105-
injectA2UITool: true,
106-
defaultCatalogId: A2UI_DOJO_CATALOG_ID,
107-
}),
101+
new A2UIMiddleware(DOJO_A2UI_MIDDLEWARE_CONFIG),
108102
);
109103
}
110104
return agents;
@@ -155,10 +149,7 @@ export const agentsIntegrations = {
155149
// Whitelist-driven per-agent A2UI injection (see ADK_A2UI_INJECT_AGENTS).
156150
for (const id of ADK_A2UI_INJECT_AGENTS) {
157151
(agents as Record<string, AbstractAgent>)[id]?.use(
158-
new A2UIMiddleware({
159-
injectA2UITool: true,
160-
defaultCatalogId: A2UI_DOJO_CATALOG_ID,
161-
}),
152+
new A2UIMiddleware(DOJO_A2UI_MIDDLEWARE_CONFIG),
162153
);
163154
}
164155
return agents;
@@ -620,10 +611,7 @@ export const agentsIntegrations = {
620611
};
621612
for (const id of STRANDS_A2UI_INJECT_AGENTS) {
622613
(agents as Record<string, AbstractAgent>)[id]?.use(
623-
new A2UIMiddleware({
624-
injectA2UITool: true,
625-
defaultCatalogId: A2UI_DOJO_CATALOG_ID,
626-
}),
614+
new A2UIMiddleware(DOJO_A2UI_MIDDLEWARE_CONFIG),
627615
);
628616
}
629617
return agents;
@@ -669,10 +657,7 @@ export const agentsIntegrations = {
669657
};
670658
for (const id of STRANDS_A2UI_INJECT_AGENTS) {
671659
(agents as Record<string, AbstractAgent>)[id]?.use(
672-
new A2UIMiddleware({
673-
injectA2UITool: true,
674-
defaultCatalogId: A2UI_DOJO_CATALOG_ID,
675-
}),
660+
new A2UIMiddleware(DOJO_A2UI_MIDDLEWARE_CONFIG),
676661
);
677662
}
678663
return agents;

0 commit comments

Comments
 (0)