bazis 0.98.34 → 0.99.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/templates/module.ts +2 -60
- package/cli/templates/passport.ts +13 -17
- package/core/agent/AgentContextBuilder.ts +4 -98
- package/core/agent/AgentRegistry.ts +79 -473
- package/core/agent/AgentRuntime.ts +56 -19
- package/core/agent/AgentToolExecutor.ts +3 -3
- package/core/agent/README.md +90 -181
- package/core/agent/index.ts +3 -31
- package/core/agent/internal/AgentExecutionDriver.ts +37 -392
- package/core/agent/internal/AgentExecutionState.ts +1 -9
- package/core/agent/metadata.ts +8 -303
- package/core/agent/semantic.ts +0 -4
- package/core/agent/session/contracts.ts +5 -7
- package/core/agent/session/entities.ts +0 -1
- package/core/generatedRuntime.ts +0 -31
- package/core/infra/connectors/codex/CodexAppServerClient.ts +1 -1
- package/core/infra/connectors/llm.ts +15 -1
- package/core/infra/connectors/llmRouter.ts +4 -0
- package/core/scripts/di-generate.ts +28 -42
- package/core/scripts/tool-codegen.ts +122 -0
- package/index.ts +3 -22
- package/library/openapi/codegen.ts +16 -5
- package/package.json +1 -1
- package/core/agent/decorators/Agent.ts +0 -11
- package/core/agent/decorators/Prompt.ts +0 -11
- package/core/agent/decorators/Task.ts +0 -29
- package/core/agent/generated/catalog.ts +0 -26
- package/core/scripts/agent-codegen.ts +0 -1166
|
@@ -1,1166 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import ts from "typescript";
|
|
3
|
-
|
|
4
|
-
export interface AgentCodegenSource {
|
|
5
|
-
readonly filePath: string;
|
|
6
|
-
/** Program-owned AST; codegen never reparses application source. */
|
|
7
|
-
readonly sourceFile?: ts.SourceFile;
|
|
8
|
-
/** Legacy test-only fixture input; project generation always supplies sourceFile. */
|
|
9
|
-
readonly content?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface AgentCodegenResult {
|
|
13
|
-
readonly output: string;
|
|
14
|
-
readonly warnings: readonly string[];
|
|
15
|
-
readonly agentCount: number;
|
|
16
|
-
readonly toolCount: number;
|
|
17
|
-
readonly promptCount: number;
|
|
18
|
-
/** Class contracts referenced by agents, tasks and tools. */
|
|
19
|
-
readonly schemaNames: readonly string[];
|
|
20
|
-
/** Program-owned declarations of those contracts, for nominal schema bindings. */
|
|
21
|
-
readonly schemaModels: readonly ts.ClassDeclaration[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface AgentCodegenOptions {
|
|
25
|
-
readonly generatedDir?: string;
|
|
26
|
-
readonly frameworkImports?: "relative" | "public";
|
|
27
|
-
/** Use the same naming policy as the project's OpenAPI analyzer. */
|
|
28
|
-
readonly schemaNameForDeclaration?: (name: string, declaration: ts.ClassDeclaration) => string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface AgentCodegenEntry {
|
|
32
|
-
readonly className: string;
|
|
33
|
-
readonly filePath: string;
|
|
34
|
-
readonly metadata: AgentCodegenMetadata;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
interface AgentCodegenMetadata {
|
|
38
|
-
readonly name: string;
|
|
39
|
-
readonly description?: string;
|
|
40
|
-
readonly role?: string;
|
|
41
|
-
readonly goal?: string;
|
|
42
|
-
readonly instructions: readonly string[];
|
|
43
|
-
readonly constraints: readonly string[];
|
|
44
|
-
readonly sections: readonly PromptSectionCodegenMetadata[];
|
|
45
|
-
readonly prompt?: string;
|
|
46
|
-
readonly tools?: readonly string[];
|
|
47
|
-
readonly input?: string;
|
|
48
|
-
readonly output?: string;
|
|
49
|
-
readonly modelProfile?: string;
|
|
50
|
-
readonly maxSteps?: number;
|
|
51
|
-
readonly tasks: readonly AgentTaskCodegenMetadata[];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
interface AgentTaskCodegenMetadata {
|
|
55
|
-
readonly name: string;
|
|
56
|
-
readonly methodName: string;
|
|
57
|
-
readonly description?: string;
|
|
58
|
-
readonly input?: string;
|
|
59
|
-
readonly output?: string;
|
|
60
|
-
readonly modelProfile?: string;
|
|
61
|
-
readonly maxSteps?: number;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
interface ToolCodegenEntry {
|
|
65
|
-
readonly className: string;
|
|
66
|
-
readonly filePath: string;
|
|
67
|
-
readonly metadata: ToolCodegenMetadata;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
interface ToolCodegenMetadata {
|
|
71
|
-
readonly name: string;
|
|
72
|
-
readonly description: string;
|
|
73
|
-
readonly input?: string;
|
|
74
|
-
readonly output?: string;
|
|
75
|
-
readonly sideEffect: "none" | "read" | "write" | "external";
|
|
76
|
-
readonly approval: "never" | "policy" | "required";
|
|
77
|
-
readonly timeoutMs?: number;
|
|
78
|
-
readonly tags: readonly string[];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
interface PromptCodegenEntry {
|
|
82
|
-
readonly className: string;
|
|
83
|
-
readonly filePath: string;
|
|
84
|
-
readonly metadata: PromptCodegenMetadata;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
interface PromptCodegenMetadata {
|
|
88
|
-
readonly name: string;
|
|
89
|
-
readonly description?: string;
|
|
90
|
-
readonly version?: string;
|
|
91
|
-
readonly role?: string;
|
|
92
|
-
readonly goal?: string;
|
|
93
|
-
readonly instructions: readonly string[];
|
|
94
|
-
readonly constraints: readonly string[];
|
|
95
|
-
readonly sections: readonly PromptSectionCodegenMetadata[];
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
type PromptSectionCodegenKind =
|
|
99
|
-
| "system"
|
|
100
|
-
| "developer"
|
|
101
|
-
| "role"
|
|
102
|
-
| "task"
|
|
103
|
-
| "instructions"
|
|
104
|
-
| "constraints"
|
|
105
|
-
| "examples"
|
|
106
|
-
| "output"
|
|
107
|
-
| "tool-policy"
|
|
108
|
-
| "memory"
|
|
109
|
-
| "knowledge";
|
|
110
|
-
|
|
111
|
-
interface PromptSectionCodegenMetadata {
|
|
112
|
-
readonly kind: PromptSectionCodegenKind;
|
|
113
|
-
readonly title?: string;
|
|
114
|
-
readonly content: readonly string[];
|
|
115
|
-
readonly priority?: number;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
interface AgentParseState {
|
|
119
|
-
readonly source: ts.SourceFile;
|
|
120
|
-
readonly node: ts.Node;
|
|
121
|
-
readonly decorator: string;
|
|
122
|
-
readonly className: string;
|
|
123
|
-
ok: boolean;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
interface AgentCodegenContext {
|
|
127
|
-
readonly generatedDir: string;
|
|
128
|
-
readonly frameworkImports: "relative" | "public";
|
|
129
|
-
readonly warnings: string[];
|
|
130
|
-
readonly classFilesByName: Map<string, string>;
|
|
131
|
-
readonly classDeclarationsByName: Map<string, ts.ClassDeclaration>;
|
|
132
|
-
readonly ambiguousClassNames: Set<string>;
|
|
133
|
-
readonly namedExportedClassNames: Set<string>;
|
|
134
|
-
readonly agents: AgentCodegenEntry[];
|
|
135
|
-
readonly tools: ToolCodegenEntry[];
|
|
136
|
-
readonly prompts: PromptCodegenEntry[];
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
interface RenderedAgentCatalog {
|
|
140
|
-
readonly output: string;
|
|
141
|
-
readonly agentCount: number;
|
|
142
|
-
readonly toolCount: number;
|
|
143
|
-
readonly promptCount: number;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export function generateAgentMetadataCatalog(
|
|
147
|
-
sources: readonly AgentCodegenSource[],
|
|
148
|
-
options: AgentCodegenOptions | string = {},
|
|
149
|
-
): AgentCodegenResult {
|
|
150
|
-
const normalizedOptions = typeof options === "string" ? { generatedDir: options } : options;
|
|
151
|
-
const context: AgentCodegenContext = {
|
|
152
|
-
generatedDir: normalizedOptions.generatedDir ?? "src/bazis/core/agent/generated",
|
|
153
|
-
frameworkImports: normalizedOptions.frameworkImports ?? "relative",
|
|
154
|
-
warnings: [],
|
|
155
|
-
classFilesByName: new Map(),
|
|
156
|
-
classDeclarationsByName: new Map(),
|
|
157
|
-
ambiguousClassNames: new Set(),
|
|
158
|
-
namedExportedClassNames: new Set(),
|
|
159
|
-
agents: [],
|
|
160
|
-
tools: [],
|
|
161
|
-
prompts: [],
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
const parsedSources = sources.map((source) => ({
|
|
165
|
-
filePath: source.filePath,
|
|
166
|
-
sourceFile: source.sourceFile ?? ts.createSourceFile(source.filePath, source.content ?? "", ts.ScriptTarget.ESNext, true, ts.ScriptKind.TS),
|
|
167
|
-
}));
|
|
168
|
-
|
|
169
|
-
for (let index = 0; index < parsedSources.length; index += 1) {
|
|
170
|
-
const source = parsedSources[index]!;
|
|
171
|
-
collectClassIndex(source.sourceFile, context);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
for (let index = 0; index < parsedSources.length; index += 1) {
|
|
175
|
-
const source = parsedSources[index]!;
|
|
176
|
-
collectAgentEntries(source.sourceFile, context);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const rendered = renderAgentCatalog(context);
|
|
180
|
-
const schemaModels = collectAgentSchemaNames(context)
|
|
181
|
-
.filter((name) => !context.ambiguousClassNames.has(name))
|
|
182
|
-
.map((name) => context.classDeclarationsByName.get(name))
|
|
183
|
-
.filter((declaration): declaration is ts.ClassDeclaration => declaration !== undefined);
|
|
184
|
-
return {
|
|
185
|
-
output: rendered.output,
|
|
186
|
-
warnings: Object.freeze([...context.warnings]),
|
|
187
|
-
agentCount: rendered.agentCount,
|
|
188
|
-
toolCount: rendered.toolCount,
|
|
189
|
-
promptCount: rendered.promptCount,
|
|
190
|
-
schemaNames: Object.freeze(schemaModels.map((declaration) =>
|
|
191
|
-
normalizedOptions.schemaNameForDeclaration?.(declaration.name!.text, declaration) ?? declaration.name!.text)
|
|
192
|
-
.sort((left, right) => left.localeCompare(right))),
|
|
193
|
-
schemaModels: Object.freeze(schemaModels),
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function collectAgentSchemaNames(context: AgentCodegenContext): readonly string[] {
|
|
198
|
-
const names = new Set<string>();
|
|
199
|
-
for (const agent of context.agents) {
|
|
200
|
-
if (agent.metadata.input) names.add(agent.metadata.input);
|
|
201
|
-
if (agent.metadata.output) names.add(agent.metadata.output);
|
|
202
|
-
for (const task of agent.metadata.tasks) {
|
|
203
|
-
if (task.input) names.add(task.input);
|
|
204
|
-
if (task.output) names.add(task.output);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
for (const tool of context.tools) {
|
|
208
|
-
if (tool.metadata.input) names.add(tool.metadata.input);
|
|
209
|
-
if (tool.metadata.output) names.add(tool.metadata.output);
|
|
210
|
-
}
|
|
211
|
-
return Object.freeze([...names].sort((left, right) => left.localeCompare(right)));
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function collectClassIndex(source: ts.SourceFile, context: AgentCodegenContext): void {
|
|
215
|
-
const visit = (node: ts.Node): void => {
|
|
216
|
-
if (ts.isClassDeclaration(node) && node.name) {
|
|
217
|
-
recordClassFile(node.name.text, source.fileName, context);
|
|
218
|
-
context.classDeclarationsByName.set(node.name.text, node);
|
|
219
|
-
if (isNamedExportedClass(node)) {
|
|
220
|
-
context.namedExportedClassNames.add(node.name.text);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
ts.forEachChild(node, visit);
|
|
224
|
-
};
|
|
225
|
-
ts.forEachChild(source, visit);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function collectAgentEntries(source: ts.SourceFile, context: AgentCodegenContext): void {
|
|
229
|
-
const visit = (node: ts.Node): void => {
|
|
230
|
-
if (ts.isClassDeclaration(node) && node.name) {
|
|
231
|
-
const className = node.name.text;
|
|
232
|
-
const decorators = ts.getDecorators(node) ?? [];
|
|
233
|
-
for (const decorator of decorators) {
|
|
234
|
-
const info = decoratorCall(decorator);
|
|
235
|
-
if (!info || (info.name !== "Agent" && info.name !== "Tool" && info.name !== "Prompt")) {
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
if (!isNamedExportedClass(node)) {
|
|
239
|
-
context.warnings.push(
|
|
240
|
-
`${info.name} class "${className}" (${source.fileName}) is not a named export; ` +
|
|
241
|
-
`agent codegen can only import named-export classes.`,
|
|
242
|
-
);
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
const namedNode = node as ts.ClassDeclaration & { readonly name: ts.Identifier };
|
|
246
|
-
if (info.name === "Agent") {
|
|
247
|
-
const metadata = parseAgentMetadata(source, namedNode, info, context);
|
|
248
|
-
if (metadata) {
|
|
249
|
-
context.agents.push({ className, filePath: source.fileName, metadata });
|
|
250
|
-
}
|
|
251
|
-
} else if (info.name === "Tool") {
|
|
252
|
-
const metadata = parseToolMetadata(source, namedNode, info, context);
|
|
253
|
-
if (metadata) {
|
|
254
|
-
context.tools.push({ className, filePath: source.fileName, metadata });
|
|
255
|
-
}
|
|
256
|
-
} else {
|
|
257
|
-
const metadata = parsePromptMetadata(source, namedNode, info, context);
|
|
258
|
-
if (metadata) {
|
|
259
|
-
context.prompts.push({ className, filePath: source.fileName, metadata });
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
ts.forEachChild(node, visit);
|
|
265
|
-
};
|
|
266
|
-
ts.forEachChild(source, visit);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function decoratorCall(decorator: ts.Decorator): { name: string; call?: ts.CallExpression } | undefined {
|
|
270
|
-
const expression = decorator.expression;
|
|
271
|
-
if (ts.isCallExpression(expression) && ts.isIdentifier(expression.expression)) {
|
|
272
|
-
return { name: expression.expression.text, call: expression };
|
|
273
|
-
}
|
|
274
|
-
if (ts.isIdentifier(expression)) {
|
|
275
|
-
return { name: expression.text };
|
|
276
|
-
}
|
|
277
|
-
return undefined;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function isNamedExportedClass(node: ts.ClassDeclaration): boolean {
|
|
281
|
-
const modifiers = ts.getModifiers(node) ?? [];
|
|
282
|
-
const hasExport = modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
|
|
283
|
-
const hasDefault = modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword);
|
|
284
|
-
return hasExport && !hasDefault;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function recordClassFile(name: string, filePath: string, context: AgentCodegenContext): void {
|
|
288
|
-
const existing = context.classFilesByName.get(name);
|
|
289
|
-
if (existing === undefined) {
|
|
290
|
-
context.classFilesByName.set(name, filePath);
|
|
291
|
-
} else if (existing !== filePath) {
|
|
292
|
-
context.ambiguousClassNames.add(name);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
function parseAgentMetadata(
|
|
297
|
-
source: ts.SourceFile,
|
|
298
|
-
node: ts.ClassDeclaration & { readonly name: ts.Identifier },
|
|
299
|
-
info: { name: string; call?: ts.CallExpression },
|
|
300
|
-
context: AgentCodegenContext,
|
|
301
|
-
): AgentCodegenMetadata | undefined {
|
|
302
|
-
const state: AgentParseState = { source, node, decorator: "@Agent", className: node.name.text, ok: true };
|
|
303
|
-
const properties = readDecoratorProperties(info, state, context, true);
|
|
304
|
-
if (!state.ok || properties === undefined) {
|
|
305
|
-
return undefined;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const name = readOptionalStringProperty(properties, "name", state, context) ?? node.name.text;
|
|
309
|
-
const description = readOptionalStringProperty(properties, "description", state, context);
|
|
310
|
-
const role = readOptionalStringProperty(properties, "role", state, context);
|
|
311
|
-
const goal = readOptionalStringProperty(properties, "goal", state, context);
|
|
312
|
-
const instructions = readOptionalStringArrayProperty(properties, "instructions", state, context) ?? [];
|
|
313
|
-
const constraints = readOptionalStringArrayProperty(properties, "constraints", state, context) ?? [];
|
|
314
|
-
const sections = readOptionalPromptSectionArrayProperty(properties, "sections", state, context) ?? [];
|
|
315
|
-
const prompt = readOptionalClassProperty(properties, "prompt", state, context);
|
|
316
|
-
const tools = readOptionalClassArrayProperty(properties, "tools", state, context) ?? [];
|
|
317
|
-
const input = readOptionalClassProperty(properties, "input", state, context);
|
|
318
|
-
const output = readOptionalClassProperty(properties, "output", state, context);
|
|
319
|
-
const modelProfile = readOptionalStringProperty(properties, "modelProfile", state, context);
|
|
320
|
-
const maxSteps = readOptionalPositiveIntegerProperty(properties, "maxSteps", state, context);
|
|
321
|
-
const tasks = parseAgentTasks(source, node, context);
|
|
322
|
-
|
|
323
|
-
if (!state.ok) {
|
|
324
|
-
return undefined;
|
|
325
|
-
}
|
|
326
|
-
return { name, description, role, goal, instructions, constraints, sections, prompt, tools, input, output, modelProfile, maxSteps, tasks };
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
function parseAgentTasks(
|
|
330
|
-
source: ts.SourceFile,
|
|
331
|
-
node: ts.ClassDeclaration & { readonly name: ts.Identifier },
|
|
332
|
-
context: AgentCodegenContext,
|
|
333
|
-
): readonly AgentTaskCodegenMetadata[] {
|
|
334
|
-
const tasks: AgentTaskCodegenMetadata[] = [];
|
|
335
|
-
for (const member of node.members) {
|
|
336
|
-
if (!ts.isMethodDeclaration(member)) {
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
const decorators = ts.getDecorators(member) ?? [];
|
|
340
|
-
for (const decorator of decorators) {
|
|
341
|
-
const info = decoratorCall(decorator);
|
|
342
|
-
if (!info || info.name !== "Task") {
|
|
343
|
-
continue;
|
|
344
|
-
}
|
|
345
|
-
const task = parseAgentTaskMetadata(source, node.name.text, member, info, context);
|
|
346
|
-
if (task !== undefined) {
|
|
347
|
-
tasks.push(task);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
return tasks;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function parseAgentTaskMetadata(
|
|
355
|
-
source: ts.SourceFile,
|
|
356
|
-
className: string,
|
|
357
|
-
node: ts.MethodDeclaration,
|
|
358
|
-
info: { name: string; call?: ts.CallExpression },
|
|
359
|
-
context: AgentCodegenContext,
|
|
360
|
-
): AgentTaskCodegenMetadata | undefined {
|
|
361
|
-
const state: AgentParseState = { source, node, decorator: "@Task", className, ok: true };
|
|
362
|
-
const methodName = methodNameText(node.name);
|
|
363
|
-
if (methodName === undefined) {
|
|
364
|
-
failAgentParse(state, context, "@Task supports string method names only.");
|
|
365
|
-
return undefined;
|
|
366
|
-
}
|
|
367
|
-
if (isStaticOrPrivateMethod(node)) {
|
|
368
|
-
failAgentParse(state, context, "@Task supports public instance methods only.");
|
|
369
|
-
return undefined;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
const properties = readTaskDecoratorProperties(info, state, context);
|
|
373
|
-
if (!state.ok || properties === undefined) {
|
|
374
|
-
return undefined;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const name = readOptionalStringProperty(properties, "name", state, context) ?? methodName;
|
|
378
|
-
const description = readOptionalStringProperty(properties, "description", state, context);
|
|
379
|
-
const input = readOptionalClassProperty(properties, "input", state, context) ?? readFirstParameterTypeName(node, state, context);
|
|
380
|
-
const output = readOptionalClassProperty(properties, "output", state, context) ?? readReturnTypeName(node, state, context);
|
|
381
|
-
const modelProfile = readOptionalStringProperty(properties, "modelProfile", state, context);
|
|
382
|
-
const maxSteps = readOptionalPositiveIntegerProperty(properties, "maxSteps", state, context);
|
|
383
|
-
|
|
384
|
-
if (!state.ok) {
|
|
385
|
-
return undefined;
|
|
386
|
-
}
|
|
387
|
-
return { name, methodName, description, input, output, modelProfile, maxSteps };
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
function readTaskDecoratorProperties(
|
|
391
|
-
info: { name: string; call?: ts.CallExpression },
|
|
392
|
-
state: AgentParseState,
|
|
393
|
-
context: AgentCodegenContext,
|
|
394
|
-
): Map<string, ts.Expression> | undefined {
|
|
395
|
-
if (!info.call) {
|
|
396
|
-
return new Map();
|
|
397
|
-
}
|
|
398
|
-
if (info.call.arguments.length === 0) {
|
|
399
|
-
return new Map();
|
|
400
|
-
}
|
|
401
|
-
const firstArg = info.call.arguments[0];
|
|
402
|
-
if (!firstArg) {
|
|
403
|
-
return new Map();
|
|
404
|
-
}
|
|
405
|
-
if (ts.isStringLiteralLike(firstArg)) {
|
|
406
|
-
return new Map([["name", firstArg]]);
|
|
407
|
-
}
|
|
408
|
-
if (!ts.isObjectLiteralExpression(firstArg)) {
|
|
409
|
-
failAgentParse(state, context, "@Task codegen supports a string literal or object literal options only.");
|
|
410
|
-
return undefined;
|
|
411
|
-
}
|
|
412
|
-
return readObjectLiteralProperties(firstArg, "@Task", state, context);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
function methodNameText(name: ts.PropertyName): string | undefined {
|
|
416
|
-
if (ts.isIdentifier(name) || ts.isStringLiteralLike(name)) {
|
|
417
|
-
return name.text;
|
|
418
|
-
}
|
|
419
|
-
return undefined;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
function isStaticOrPrivateMethod(node: ts.MethodDeclaration): boolean {
|
|
423
|
-
const modifiers = ts.getModifiers(node) ?? [];
|
|
424
|
-
return modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword || modifier.kind === ts.SyntaxKind.PrivateKeyword);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
function readFirstParameterTypeName(
|
|
428
|
-
node: ts.MethodDeclaration,
|
|
429
|
-
state: AgentParseState,
|
|
430
|
-
context: AgentCodegenContext,
|
|
431
|
-
): string | undefined {
|
|
432
|
-
const first = node.parameters[0];
|
|
433
|
-
if (!first?.type) {
|
|
434
|
-
return undefined;
|
|
435
|
-
}
|
|
436
|
-
return readTypeReferenceName(first.type, "task input", state, context);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
function readReturnTypeName(
|
|
440
|
-
node: ts.MethodDeclaration,
|
|
441
|
-
state: AgentParseState,
|
|
442
|
-
context: AgentCodegenContext,
|
|
443
|
-
): string | undefined {
|
|
444
|
-
if (!node.type) {
|
|
445
|
-
return undefined;
|
|
446
|
-
}
|
|
447
|
-
return readTypeReferenceName(node.type, "task output", state, context);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
function readTypeReferenceName(
|
|
451
|
-
type: ts.TypeNode,
|
|
452
|
-
field: string,
|
|
453
|
-
state: AgentParseState,
|
|
454
|
-
context: AgentCodegenContext,
|
|
455
|
-
): string | undefined {
|
|
456
|
-
if (!ts.isTypeReferenceNode(type)) {
|
|
457
|
-
failAgentParse(state, context, `${field} must be a class type reference.`);
|
|
458
|
-
return undefined;
|
|
459
|
-
}
|
|
460
|
-
if (!ts.isIdentifier(type.typeName)) {
|
|
461
|
-
failAgentParse(state, context, `${field} must be a class type identifier.`);
|
|
462
|
-
return undefined;
|
|
463
|
-
}
|
|
464
|
-
if (type.typeName.text === "Promise") {
|
|
465
|
-
const inner = type.typeArguments?.[0];
|
|
466
|
-
if (!inner) {
|
|
467
|
-
failAgentParse(state, context, `${field} Promise<T> must declare T.`);
|
|
468
|
-
return undefined;
|
|
469
|
-
}
|
|
470
|
-
return readTypeReferenceName(inner, field, state, context);
|
|
471
|
-
}
|
|
472
|
-
return type.typeName.text;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
function parseToolMetadata(
|
|
476
|
-
source: ts.SourceFile,
|
|
477
|
-
node: ts.ClassDeclaration & { readonly name: ts.Identifier },
|
|
478
|
-
info: { name: string; call?: ts.CallExpression },
|
|
479
|
-
context: AgentCodegenContext,
|
|
480
|
-
): ToolCodegenMetadata | undefined {
|
|
481
|
-
const state: AgentParseState = { source, node, decorator: "@Tool", className: node.name.text, ok: true };
|
|
482
|
-
const properties = readDecoratorProperties(info, state, context, false);
|
|
483
|
-
if (!state.ok || properties === undefined) {
|
|
484
|
-
return undefined;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
const name = readRequiredStringProperty(properties, "name", state, context);
|
|
488
|
-
const description = readRequiredStringProperty(properties, "description", state, context);
|
|
489
|
-
const input = readOptionalClassProperty(properties, "input", state, context);
|
|
490
|
-
const output = readOptionalClassProperty(properties, "output", state, context);
|
|
491
|
-
const sideEffect = readOptionalStringProperty(properties, "sideEffect", state, context) ?? "none";
|
|
492
|
-
const approval = readOptionalStringProperty(properties, "approval", state, context);
|
|
493
|
-
const timeoutMs = readOptionalPositiveIntegerProperty(properties, "timeoutMs", state, context);
|
|
494
|
-
const tags = readOptionalStringArrayProperty(properties, "tags", state, context) ?? [];
|
|
495
|
-
|
|
496
|
-
if (sideEffect !== "none" && sideEffect !== "read" && sideEffect !== "write" && sideEffect !== "external") {
|
|
497
|
-
failAgentParse(state, context, `sideEffect must be one of "none", "read", "write", "external".`);
|
|
498
|
-
}
|
|
499
|
-
if (approval !== undefined && approval !== "never" && approval !== "policy" && approval !== "required") {
|
|
500
|
-
failAgentParse(state, context, `approval must be one of "never", "policy", "required".`);
|
|
501
|
-
}
|
|
502
|
-
if ((sideEffect === "write" || sideEffect === "external") && approval === "never") {
|
|
503
|
-
failAgentParse(state, context, `write/external tools cannot use approval: "never".`);
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
if (!state.ok || name === undefined || description === undefined) {
|
|
507
|
-
return undefined;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
const resolvedSideEffect = sideEffect as ToolCodegenMetadata["sideEffect"];
|
|
511
|
-
const resolvedApproval = (approval ??
|
|
512
|
-
(sideEffect === "write" || sideEffect === "external" ? "required" : "policy")) as ToolCodegenMetadata["approval"];
|
|
513
|
-
return {
|
|
514
|
-
name,
|
|
515
|
-
description,
|
|
516
|
-
input,
|
|
517
|
-
output,
|
|
518
|
-
sideEffect: resolvedSideEffect,
|
|
519
|
-
approval: resolvedApproval,
|
|
520
|
-
timeoutMs,
|
|
521
|
-
tags,
|
|
522
|
-
};
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
function parsePromptMetadata(
|
|
526
|
-
source: ts.SourceFile,
|
|
527
|
-
node: ts.ClassDeclaration & { readonly name: ts.Identifier },
|
|
528
|
-
info: { name: string; call?: ts.CallExpression },
|
|
529
|
-
context: AgentCodegenContext,
|
|
530
|
-
): PromptCodegenMetadata | undefined {
|
|
531
|
-
const state: AgentParseState = { source, node, decorator: "@Prompt", className: node.name.text, ok: true };
|
|
532
|
-
const properties = readDecoratorProperties(info, state, context, true);
|
|
533
|
-
if (!state.ok || properties === undefined) {
|
|
534
|
-
return undefined;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
const name = readOptionalStringProperty(properties, "name", state, context) ?? node.name.text;
|
|
538
|
-
const description = readOptionalStringProperty(properties, "description", state, context);
|
|
539
|
-
const version = readOptionalStringProperty(properties, "version", state, context);
|
|
540
|
-
const role = readOptionalStringProperty(properties, "role", state, context);
|
|
541
|
-
const goal = readOptionalStringProperty(properties, "goal", state, context);
|
|
542
|
-
const instructions = readOptionalStringArrayProperty(properties, "instructions", state, context) ?? [];
|
|
543
|
-
const constraints = readOptionalStringArrayProperty(properties, "constraints", state, context) ?? [];
|
|
544
|
-
const sections = readOptionalPromptSectionArrayProperty(properties, "sections", state, context) ?? [];
|
|
545
|
-
|
|
546
|
-
if (!state.ok) {
|
|
547
|
-
return undefined;
|
|
548
|
-
}
|
|
549
|
-
return { name, description, version, role, goal, instructions, constraints, sections };
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
function readDecoratorProperties(
|
|
553
|
-
info: { name: string; call?: ts.CallExpression },
|
|
554
|
-
state: AgentParseState,
|
|
555
|
-
context: AgentCodegenContext,
|
|
556
|
-
allowEmpty: boolean,
|
|
557
|
-
): Map<string, ts.Expression> | undefined {
|
|
558
|
-
if (!info.call) {
|
|
559
|
-
failAgentParse(state, context, `${state.decorator} must be called as ${state.decorator}(...).`);
|
|
560
|
-
return undefined;
|
|
561
|
-
}
|
|
562
|
-
if (info.call.arguments.length === 0) {
|
|
563
|
-
if (allowEmpty) {
|
|
564
|
-
return new Map();
|
|
565
|
-
}
|
|
566
|
-
failAgentParse(state, context, `${state.decorator} requires an object literal argument.`);
|
|
567
|
-
return undefined;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
const firstArg = info.call.arguments[0];
|
|
571
|
-
if (!firstArg || !ts.isObjectLiteralExpression(firstArg)) {
|
|
572
|
-
failAgentParse(state, context, `${state.decorator} codegen supports object literal options only.`);
|
|
573
|
-
return undefined;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
const properties = new Map<string, ts.Expression>();
|
|
577
|
-
for (const property of firstArg.properties) {
|
|
578
|
-
if (!ts.isPropertyAssignment(property)) {
|
|
579
|
-
failAgentParse(state, context, `${state.decorator} codegen does not support shorthand or spread properties.`);
|
|
580
|
-
return undefined;
|
|
581
|
-
}
|
|
582
|
-
const name = propertyNameText(property.name);
|
|
583
|
-
if (name === undefined) {
|
|
584
|
-
failAgentParse(state, context, `${state.decorator} contains an unsupported property name.`);
|
|
585
|
-
return undefined;
|
|
586
|
-
}
|
|
587
|
-
properties.set(name, property.initializer);
|
|
588
|
-
}
|
|
589
|
-
return properties;
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
function readObjectLiteralProperties(
|
|
593
|
-
expression: ts.Expression,
|
|
594
|
-
name: string,
|
|
595
|
-
state: AgentParseState,
|
|
596
|
-
context: AgentCodegenContext,
|
|
597
|
-
): Map<string, ts.Expression> | undefined {
|
|
598
|
-
if (!ts.isObjectLiteralExpression(expression)) {
|
|
599
|
-
failAgentParse(state, context, `${name} must be an object literal.`);
|
|
600
|
-
return undefined;
|
|
601
|
-
}
|
|
602
|
-
const properties = new Map<string, ts.Expression>();
|
|
603
|
-
for (const property of expression.properties) {
|
|
604
|
-
if (!ts.isPropertyAssignment(property)) {
|
|
605
|
-
failAgentParse(state, context, `${name} codegen does not support shorthand or spread properties.`);
|
|
606
|
-
return undefined;
|
|
607
|
-
}
|
|
608
|
-
const propertyName = propertyNameText(property.name);
|
|
609
|
-
if (propertyName === undefined) {
|
|
610
|
-
failAgentParse(state, context, `${name} contains an unsupported property name.`);
|
|
611
|
-
return undefined;
|
|
612
|
-
}
|
|
613
|
-
properties.set(propertyName, property.initializer);
|
|
614
|
-
}
|
|
615
|
-
return properties;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
function propertyNameText(name: ts.PropertyName): string | undefined {
|
|
619
|
-
if (ts.isIdentifier(name) || ts.isStringLiteralLike(name)) {
|
|
620
|
-
return name.text;
|
|
621
|
-
}
|
|
622
|
-
return undefined;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
function readRequiredStringProperty(
|
|
626
|
-
properties: ReadonlyMap<string, ts.Expression>,
|
|
627
|
-
name: string,
|
|
628
|
-
state: AgentParseState,
|
|
629
|
-
context: AgentCodegenContext,
|
|
630
|
-
): string | undefined {
|
|
631
|
-
const expression = properties.get(name);
|
|
632
|
-
if (!expression) {
|
|
633
|
-
failAgentParse(state, context, `${name} is required.`);
|
|
634
|
-
return undefined;
|
|
635
|
-
}
|
|
636
|
-
return readStringExpression(expression, name, state, context);
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
function readOptionalStringProperty(
|
|
640
|
-
properties: ReadonlyMap<string, ts.Expression>,
|
|
641
|
-
name: string,
|
|
642
|
-
state: AgentParseState,
|
|
643
|
-
context: AgentCodegenContext,
|
|
644
|
-
): string | undefined {
|
|
645
|
-
const expression = properties.get(name);
|
|
646
|
-
if (!expression) {
|
|
647
|
-
return undefined;
|
|
648
|
-
}
|
|
649
|
-
return readStringExpression(expression, name, state, context);
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
function readStringExpression(
|
|
653
|
-
expression: ts.Expression,
|
|
654
|
-
name: string,
|
|
655
|
-
state: AgentParseState,
|
|
656
|
-
context: AgentCodegenContext,
|
|
657
|
-
): string | undefined {
|
|
658
|
-
if (!ts.isStringLiteralLike(expression)) {
|
|
659
|
-
failAgentParse(state, context, `${name} must be a string literal.`);
|
|
660
|
-
return undefined;
|
|
661
|
-
}
|
|
662
|
-
const value = expression.text.trim();
|
|
663
|
-
if (value.length === 0) {
|
|
664
|
-
failAgentParse(state, context, `${name} must be a non-empty string.`);
|
|
665
|
-
return undefined;
|
|
666
|
-
}
|
|
667
|
-
return value;
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
function readOptionalStringArrayProperty(
|
|
671
|
-
properties: ReadonlyMap<string, ts.Expression>,
|
|
672
|
-
name: string,
|
|
673
|
-
state: AgentParseState,
|
|
674
|
-
context: AgentCodegenContext,
|
|
675
|
-
): readonly string[] | undefined {
|
|
676
|
-
const expression = properties.get(name);
|
|
677
|
-
if (!expression) {
|
|
678
|
-
return undefined;
|
|
679
|
-
}
|
|
680
|
-
if (!ts.isArrayLiteralExpression(expression)) {
|
|
681
|
-
failAgentParse(state, context, `${name} must be an array literal.`);
|
|
682
|
-
return undefined;
|
|
683
|
-
}
|
|
684
|
-
const values: string[] = [];
|
|
685
|
-
for (const element of expression.elements) {
|
|
686
|
-
const value = readStringExpression(element, name, state, context);
|
|
687
|
-
if (value !== undefined) {
|
|
688
|
-
values.push(value);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
return values;
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
function readOptionalPromptSectionArrayProperty(
|
|
695
|
-
properties: ReadonlyMap<string, ts.Expression>,
|
|
696
|
-
name: string,
|
|
697
|
-
state: AgentParseState,
|
|
698
|
-
context: AgentCodegenContext,
|
|
699
|
-
): readonly PromptSectionCodegenMetadata[] | undefined {
|
|
700
|
-
const expression = properties.get(name);
|
|
701
|
-
if (!expression) {
|
|
702
|
-
return undefined;
|
|
703
|
-
}
|
|
704
|
-
if (!ts.isArrayLiteralExpression(expression)) {
|
|
705
|
-
failAgentParse(state, context, `${name} must be an array literal.`);
|
|
706
|
-
return undefined;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
const values: PromptSectionCodegenMetadata[] = [];
|
|
710
|
-
for (let index = 0; index < expression.elements.length; index += 1) {
|
|
711
|
-
const element = expression.elements[index] as ts.Expression;
|
|
712
|
-
const sectionProperties = readObjectLiteralProperties(element, `${name}[${index}]`, state, context);
|
|
713
|
-
if (sectionProperties === undefined) {
|
|
714
|
-
continue;
|
|
715
|
-
}
|
|
716
|
-
const kind = readRequiredPromptSectionKindProperty(sectionProperties, "kind", state, context);
|
|
717
|
-
const title = readOptionalStringProperty(sectionProperties, "title", state, context);
|
|
718
|
-
const content = readRequiredStringOrStringArrayProperty(sectionProperties, "content", state, context);
|
|
719
|
-
const priority = readOptionalPositiveIntegerProperty(sectionProperties, "priority", state, context);
|
|
720
|
-
if (kind !== undefined && content !== undefined) {
|
|
721
|
-
values.push({ kind, title, content, priority });
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
return values;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
function readRequiredPromptSectionKindProperty(
|
|
728
|
-
properties: ReadonlyMap<string, ts.Expression>,
|
|
729
|
-
name: string,
|
|
730
|
-
state: AgentParseState,
|
|
731
|
-
context: AgentCodegenContext,
|
|
732
|
-
): PromptSectionCodegenKind | undefined {
|
|
733
|
-
const value = readRequiredStringProperty(properties, name, state, context);
|
|
734
|
-
if (value === undefined) {
|
|
735
|
-
return undefined;
|
|
736
|
-
}
|
|
737
|
-
if (!isPromptSectionKind(value)) {
|
|
738
|
-
failAgentParse(
|
|
739
|
-
state,
|
|
740
|
-
context,
|
|
741
|
-
`${name} must be one of "system", "developer", "role", "task", "instructions", "constraints", "examples", "output", "tool-policy", "memory", "knowledge".`,
|
|
742
|
-
);
|
|
743
|
-
return undefined;
|
|
744
|
-
}
|
|
745
|
-
return value;
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
function isPromptSectionKind(value: string): value is PromptSectionCodegenKind {
|
|
749
|
-
return (
|
|
750
|
-
value === "system" ||
|
|
751
|
-
value === "developer" ||
|
|
752
|
-
value === "role" ||
|
|
753
|
-
value === "task" ||
|
|
754
|
-
value === "instructions" ||
|
|
755
|
-
value === "constraints" ||
|
|
756
|
-
value === "examples" ||
|
|
757
|
-
value === "output" ||
|
|
758
|
-
value === "tool-policy" ||
|
|
759
|
-
value === "memory" ||
|
|
760
|
-
value === "knowledge"
|
|
761
|
-
);
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
function readRequiredStringOrStringArrayProperty(
|
|
765
|
-
properties: ReadonlyMap<string, ts.Expression>,
|
|
766
|
-
name: string,
|
|
767
|
-
state: AgentParseState,
|
|
768
|
-
context: AgentCodegenContext,
|
|
769
|
-
): readonly string[] | undefined {
|
|
770
|
-
const expression = properties.get(name);
|
|
771
|
-
if (!expression) {
|
|
772
|
-
failAgentParse(state, context, `${name} is required.`);
|
|
773
|
-
return undefined;
|
|
774
|
-
}
|
|
775
|
-
if (ts.isStringLiteralLike(expression)) {
|
|
776
|
-
const value = readStringExpression(expression, name, state, context);
|
|
777
|
-
return value === undefined ? undefined : [value];
|
|
778
|
-
}
|
|
779
|
-
if (ts.isArrayLiteralExpression(expression)) {
|
|
780
|
-
const values: string[] = [];
|
|
781
|
-
for (const element of expression.elements) {
|
|
782
|
-
const value = readStringExpression(element, name, state, context);
|
|
783
|
-
if (value !== undefined) {
|
|
784
|
-
values.push(value);
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
return values;
|
|
788
|
-
}
|
|
789
|
-
failAgentParse(state, context, `${name} must be a string literal or an array literal.`);
|
|
790
|
-
return undefined;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
function readOptionalClassProperty(
|
|
794
|
-
properties: ReadonlyMap<string, ts.Expression>,
|
|
795
|
-
name: string,
|
|
796
|
-
state: AgentParseState,
|
|
797
|
-
context: AgentCodegenContext,
|
|
798
|
-
): string | undefined {
|
|
799
|
-
const expression = properties.get(name);
|
|
800
|
-
if (!expression) {
|
|
801
|
-
return undefined;
|
|
802
|
-
}
|
|
803
|
-
return readClassReferenceExpression(expression, name, state, context);
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
function readOptionalClassArrayProperty(
|
|
807
|
-
properties: ReadonlyMap<string, ts.Expression>,
|
|
808
|
-
name: string,
|
|
809
|
-
state: AgentParseState,
|
|
810
|
-
context: AgentCodegenContext,
|
|
811
|
-
): readonly string[] | undefined {
|
|
812
|
-
const expression = properties.get(name);
|
|
813
|
-
if (!expression) {
|
|
814
|
-
return undefined;
|
|
815
|
-
}
|
|
816
|
-
if (!ts.isArrayLiteralExpression(expression)) {
|
|
817
|
-
failAgentParse(state, context, `${name} must be an array literal.`);
|
|
818
|
-
return undefined;
|
|
819
|
-
}
|
|
820
|
-
const values: string[] = [];
|
|
821
|
-
for (const element of expression.elements) {
|
|
822
|
-
const value = readClassReferenceExpression(element, name, state, context);
|
|
823
|
-
if (value !== undefined) {
|
|
824
|
-
values.push(value);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
return values;
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
function readClassReferenceExpression(
|
|
831
|
-
expression: ts.Expression,
|
|
832
|
-
name: string,
|
|
833
|
-
state: AgentParseState,
|
|
834
|
-
context: AgentCodegenContext,
|
|
835
|
-
): string | undefined {
|
|
836
|
-
if (!ts.isIdentifier(expression)) {
|
|
837
|
-
failAgentParse(state, context, `${name} must be a class identifier.`);
|
|
838
|
-
return undefined;
|
|
839
|
-
}
|
|
840
|
-
return expression.text;
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
function readOptionalPositiveIntegerProperty(
|
|
844
|
-
properties: ReadonlyMap<string, ts.Expression>,
|
|
845
|
-
name: string,
|
|
846
|
-
state: AgentParseState,
|
|
847
|
-
context: AgentCodegenContext,
|
|
848
|
-
): number | undefined {
|
|
849
|
-
const expression = properties.get(name);
|
|
850
|
-
if (!expression) {
|
|
851
|
-
return undefined;
|
|
852
|
-
}
|
|
853
|
-
if (!ts.isNumericLiteral(expression)) {
|
|
854
|
-
failAgentParse(state, context, `${name} must be a positive integer literal.`);
|
|
855
|
-
return undefined;
|
|
856
|
-
}
|
|
857
|
-
const value = Number(expression.text);
|
|
858
|
-
if (!Number.isInteger(value) || value <= 0) {
|
|
859
|
-
failAgentParse(state, context, `${name} must be a positive integer literal.`);
|
|
860
|
-
return undefined;
|
|
861
|
-
}
|
|
862
|
-
return value;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
function failAgentParse(state: AgentParseState, context: AgentCodegenContext, message: string): void {
|
|
866
|
-
state.ok = false;
|
|
867
|
-
context.warnings.push(`${state.decorator} ${state.className} (${sourceLocation(state.source, state.node)}): ${message}`);
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
function sourceLocation(source: ts.SourceFile, node: ts.Node): string {
|
|
871
|
-
const position = source.getLineAndCharacterOfPosition(node.getStart(source));
|
|
872
|
-
return `${source.fileName}:${position.line + 1}:${position.character + 1}`;
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
function renderAgentCatalog(context: AgentCodegenContext): RenderedAgentCatalog {
|
|
876
|
-
const imports = new Map<string, Set<string>>();
|
|
877
|
-
const validAgents = filterImportableAgentEntries(context.agents, imports, context);
|
|
878
|
-
const validTools = filterImportableToolEntries(context.tools, imports, context);
|
|
879
|
-
const validPrompts = filterImportablePromptEntries(context.prompts, imports, context);
|
|
880
|
-
|
|
881
|
-
const lines: string[] = [];
|
|
882
|
-
lines.push("// This file is auto-generated by `bazis codegen`.");
|
|
883
|
-
lines.push("// Do not edit manually.");
|
|
884
|
-
lines.push("");
|
|
885
|
-
if (context.frameworkImports === "public") {
|
|
886
|
-
lines.push('import type { Class } from "bazis/core/di";');
|
|
887
|
-
lines.push('import type { AgentMetadata, AgentMetadataIndex, PromptMetadata, ToolMetadata } from "bazis/core/agent";');
|
|
888
|
-
} else {
|
|
889
|
-
// The core catalog ships inside the standalone `bazis` package, where
|
|
890
|
-
// workspace-only `@/` aliases do not exist.
|
|
891
|
-
lines.push('import type { Class } from "../../di";');
|
|
892
|
-
lines.push('import type { AgentMetadataIndex } from "../AgentRegistry";');
|
|
893
|
-
lines.push('import type { AgentMetadata, PromptMetadata, ToolMetadata } from "../metadata";');
|
|
894
|
-
}
|
|
895
|
-
for (const importPath of [...imports.keys()].sort((a, b) => a.localeCompare(b))) {
|
|
896
|
-
const classNames = [...(imports.get(importPath) as Set<string>)].sort((a, b) => a.localeCompare(b));
|
|
897
|
-
lines.push(`import { ${classNames.join(", ")} } from ${JSON.stringify(importPath)};`);
|
|
898
|
-
}
|
|
899
|
-
lines.push("");
|
|
900
|
-
lines.push("function readonlyMetadataMap<K, V>(entries: readonly (readonly [K, V])[]): ReadonlyMap<K, V> {");
|
|
901
|
-
lines.push(" const lookup = new Map<K, V>(entries);");
|
|
902
|
-
lines.push(" const facade: ReadonlyMap<K, V> = {");
|
|
903
|
-
lines.push(" get size() { return lookup.size; }, has: (key) => lookup.has(key), get: (key) => lookup.get(key),");
|
|
904
|
-
lines.push(" entries: () => lookup.entries(), keys: () => lookup.keys(), values: () => lookup.values(),");
|
|
905
|
-
lines.push(" forEach: (callback, thisArg) => { for (const [key, value] of lookup) callback.call(thisArg, value, key, facade); },");
|
|
906
|
-
lines.push(" [Symbol.iterator]: () => lookup[Symbol.iterator](),");
|
|
907
|
-
lines.push(" };");
|
|
908
|
-
lines.push(" return Object.freeze(facade);");
|
|
909
|
-
lines.push("}");
|
|
910
|
-
lines.push("");
|
|
911
|
-
lines.push("export const GENERATED_AGENT_METADATA: AgentMetadataIndex = Object.freeze({");
|
|
912
|
-
lines.push(" agents: readonlyMetadataMap<Class<object>, AgentMetadata>([");
|
|
913
|
-
for (const entry of validAgents) {
|
|
914
|
-
lines.push(` [${entry.className}, Object.freeze(${renderAgentMetadataLiteral(entry.metadata)})],`);
|
|
915
|
-
}
|
|
916
|
-
lines.push(" ]),");
|
|
917
|
-
lines.push(" tools: readonlyMetadataMap<Class<object>, ToolMetadata>([");
|
|
918
|
-
for (const entry of validTools) {
|
|
919
|
-
lines.push(` [${entry.className}, Object.freeze(${renderToolMetadataLiteral(entry.metadata)})],`);
|
|
920
|
-
}
|
|
921
|
-
lines.push(" ]),");
|
|
922
|
-
lines.push(" prompts: readonlyMetadataMap<Class<object>, PromptMetadata>([");
|
|
923
|
-
for (const entry of validPrompts) {
|
|
924
|
-
lines.push(` [${entry.className}, Object.freeze(${renderPromptMetadataLiteral(entry.metadata)})],`);
|
|
925
|
-
}
|
|
926
|
-
lines.push(" ]),");
|
|
927
|
-
lines.push("});");
|
|
928
|
-
lines.push("");
|
|
929
|
-
return {
|
|
930
|
-
output: lines.join("\n"),
|
|
931
|
-
agentCount: validAgents.length,
|
|
932
|
-
toolCount: validTools.length,
|
|
933
|
-
promptCount: validPrompts.length,
|
|
934
|
-
};
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
function filterImportableAgentEntries(
|
|
938
|
-
entries: readonly AgentCodegenEntry[],
|
|
939
|
-
imports: Map<string, Set<string>>,
|
|
940
|
-
context: AgentCodegenContext,
|
|
941
|
-
): AgentCodegenEntry[] {
|
|
942
|
-
const result: AgentCodegenEntry[] = [];
|
|
943
|
-
for (const entry of entries) {
|
|
944
|
-
if (!canImportClass(entry.className, entry.className, "agent class", context)) {
|
|
945
|
-
continue;
|
|
946
|
-
}
|
|
947
|
-
const refs = [
|
|
948
|
-
entry.metadata.prompt,
|
|
949
|
-
entry.metadata.input,
|
|
950
|
-
entry.metadata.output,
|
|
951
|
-
...(entry.metadata.tools ?? []),
|
|
952
|
-
...entry.metadata.tasks.flatMap((task) => [task.input, task.output]),
|
|
953
|
-
].filter((value): value is string => value !== undefined);
|
|
954
|
-
if (!canImportAllReferences(refs, entry.className, context)) {
|
|
955
|
-
continue;
|
|
956
|
-
}
|
|
957
|
-
addGeneratedImport(imports, entry.className, entry.filePath, context);
|
|
958
|
-
addGeneratedImportsByName(imports, refs, context);
|
|
959
|
-
result.push(entry);
|
|
960
|
-
}
|
|
961
|
-
return result;
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
function filterImportableToolEntries(
|
|
965
|
-
entries: readonly ToolCodegenEntry[],
|
|
966
|
-
imports: Map<string, Set<string>>,
|
|
967
|
-
context: AgentCodegenContext,
|
|
968
|
-
): ToolCodegenEntry[] {
|
|
969
|
-
const result: ToolCodegenEntry[] = [];
|
|
970
|
-
for (const entry of entries) {
|
|
971
|
-
if (!canImportClass(entry.className, entry.className, "tool class", context)) {
|
|
972
|
-
continue;
|
|
973
|
-
}
|
|
974
|
-
const refs = [entry.metadata.input, entry.metadata.output].filter((value): value is string => value !== undefined);
|
|
975
|
-
if (!canImportAllReferences(refs, entry.className, context)) {
|
|
976
|
-
continue;
|
|
977
|
-
}
|
|
978
|
-
addGeneratedImport(imports, entry.className, entry.filePath, context);
|
|
979
|
-
addGeneratedImportsByName(imports, refs, context);
|
|
980
|
-
result.push(entry);
|
|
981
|
-
}
|
|
982
|
-
return result;
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
function filterImportablePromptEntries(
|
|
986
|
-
entries: readonly PromptCodegenEntry[],
|
|
987
|
-
imports: Map<string, Set<string>>,
|
|
988
|
-
context: AgentCodegenContext,
|
|
989
|
-
): PromptCodegenEntry[] {
|
|
990
|
-
const result: PromptCodegenEntry[] = [];
|
|
991
|
-
for (const entry of entries) {
|
|
992
|
-
if (!canImportClass(entry.className, entry.className, "prompt class", context)) {
|
|
993
|
-
continue;
|
|
994
|
-
}
|
|
995
|
-
addGeneratedImport(imports, entry.className, entry.filePath, context);
|
|
996
|
-
result.push(entry);
|
|
997
|
-
}
|
|
998
|
-
return result;
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
function canImportAllReferences(
|
|
1002
|
-
classNames: readonly string[],
|
|
1003
|
-
owner: string,
|
|
1004
|
-
context: AgentCodegenContext,
|
|
1005
|
-
): boolean {
|
|
1006
|
-
for (let index = 0; index < classNames.length; index += 1) {
|
|
1007
|
-
const className = classNames[index] as string;
|
|
1008
|
-
if (!canImportClass(className, owner, "referenced class", context)) {
|
|
1009
|
-
return false;
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
return true;
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
function canImportClass(className: string, owner: string, role: string, context: AgentCodegenContext): boolean {
|
|
1016
|
-
if (context.ambiguousClassNames.has(className)) {
|
|
1017
|
-
context.warnings.push(`agent codegen: ${owner} uses ambiguous ${role} "${className}" declared in more than one file.`);
|
|
1018
|
-
return false;
|
|
1019
|
-
}
|
|
1020
|
-
if (!context.classFilesByName.has(className)) {
|
|
1021
|
-
context.warnings.push(`agent codegen: ${owner} references ${role} "${className}", but it was not found in scanned sources.`);
|
|
1022
|
-
return false;
|
|
1023
|
-
}
|
|
1024
|
-
if (!context.namedExportedClassNames.has(className)) {
|
|
1025
|
-
context.warnings.push(`agent codegen: ${owner} references ${role} "${className}", but it is not a named export.`);
|
|
1026
|
-
return false;
|
|
1027
|
-
}
|
|
1028
|
-
return true;
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
function addGeneratedImportsByName(
|
|
1032
|
-
imports: Map<string, Set<string>>,
|
|
1033
|
-
classNames: readonly string[],
|
|
1034
|
-
context: AgentCodegenContext,
|
|
1035
|
-
): void {
|
|
1036
|
-
for (let index = 0; index < classNames.length; index += 1) {
|
|
1037
|
-
const className = classNames[index] as string;
|
|
1038
|
-
const filePath = context.classFilesByName.get(className);
|
|
1039
|
-
if (filePath !== undefined) {
|
|
1040
|
-
addGeneratedImport(imports, className, filePath, context);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
function addGeneratedImport(
|
|
1046
|
-
imports: Map<string, Set<string>>,
|
|
1047
|
-
className: string,
|
|
1048
|
-
filePath: string,
|
|
1049
|
-
context: AgentCodegenContext,
|
|
1050
|
-
): void {
|
|
1051
|
-
const importPath = toModuleSpecifierFrom(context.generatedDir, filePath);
|
|
1052
|
-
let classNames = imports.get(importPath);
|
|
1053
|
-
if (!classNames) {
|
|
1054
|
-
classNames = new Set<string>();
|
|
1055
|
-
imports.set(importPath, classNames);
|
|
1056
|
-
}
|
|
1057
|
-
classNames.add(className);
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
function toModuleSpecifierFrom(fromDir: string, filePath: string): string {
|
|
1061
|
-
const withoutExt = filePath.replace(/\.tsx?$/, "");
|
|
1062
|
-
let relative = path.relative(fromDir, withoutExt).replaceAll("\\", "/");
|
|
1063
|
-
if (!relative.startsWith(".")) {
|
|
1064
|
-
relative = `./${relative}`;
|
|
1065
|
-
}
|
|
1066
|
-
return relative;
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
function renderAgentMetadataLiteral(metadata: AgentCodegenMetadata): string {
|
|
1070
|
-
const properties = [
|
|
1071
|
-
`name: ${JSON.stringify(metadata.name)}`,
|
|
1072
|
-
renderOptionalStringProperty("description", metadata.description),
|
|
1073
|
-
renderOptionalStringProperty("role", metadata.role),
|
|
1074
|
-
renderOptionalStringProperty("goal", metadata.goal),
|
|
1075
|
-
`instructions: ${renderStringArray(metadata.instructions)}`,
|
|
1076
|
-
`constraints: ${renderStringArray(metadata.constraints)}`,
|
|
1077
|
-
`sections: ${renderPromptSections(metadata.sections)}`,
|
|
1078
|
-
renderOptionalClassProperty("prompt", metadata.prompt),
|
|
1079
|
-
`tools: ${renderClassArray(metadata.tools ?? [])}`,
|
|
1080
|
-
renderOptionalClassProperty("input", metadata.input),
|
|
1081
|
-
renderOptionalClassProperty("output", metadata.output),
|
|
1082
|
-
renderOptionalStringProperty("modelProfile", metadata.modelProfile),
|
|
1083
|
-
renderOptionalNumberProperty("maxSteps", metadata.maxSteps),
|
|
1084
|
-
`tasks: ${renderAgentTasks(metadata.tasks)}`,
|
|
1085
|
-
].filter((line): line is string => line !== undefined);
|
|
1086
|
-
return `{ ${properties.join(", ")} }`;
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
function renderAgentTasks(values: readonly AgentTaskCodegenMetadata[]): string {
|
|
1090
|
-
return `Object.freeze([${values.map((value) => `Object.freeze(${renderAgentTaskLiteral(value)})`).join(", ")}])`;
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
function renderAgentTaskLiteral(value: AgentTaskCodegenMetadata): string {
|
|
1094
|
-
const properties = [
|
|
1095
|
-
`name: ${JSON.stringify(value.name)}`,
|
|
1096
|
-
`methodName: ${JSON.stringify(value.methodName)}`,
|
|
1097
|
-
renderOptionalStringProperty("description", value.description),
|
|
1098
|
-
renderOptionalClassProperty("input", value.input),
|
|
1099
|
-
renderOptionalClassProperty("output", value.output),
|
|
1100
|
-
renderOptionalStringProperty("modelProfile", value.modelProfile),
|
|
1101
|
-
renderOptionalNumberProperty("maxSteps", value.maxSteps),
|
|
1102
|
-
].filter((line): line is string => line !== undefined);
|
|
1103
|
-
return `{ ${properties.join(", ")} }`;
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
function renderToolMetadataLiteral(metadata: ToolCodegenMetadata): string {
|
|
1107
|
-
const properties = [
|
|
1108
|
-
`name: ${JSON.stringify(metadata.name)}`,
|
|
1109
|
-
`description: ${JSON.stringify(metadata.description)}`,
|
|
1110
|
-
renderOptionalClassProperty("input", metadata.input),
|
|
1111
|
-
renderOptionalClassProperty("output", metadata.output),
|
|
1112
|
-
`sideEffect: ${JSON.stringify(metadata.sideEffect)}`,
|
|
1113
|
-
`approval: ${JSON.stringify(metadata.approval)}`,
|
|
1114
|
-
renderOptionalNumberProperty("timeoutMs", metadata.timeoutMs),
|
|
1115
|
-
`tags: ${renderStringArray(metadata.tags)}`,
|
|
1116
|
-
].filter((line): line is string => line !== undefined);
|
|
1117
|
-
return `{ ${properties.join(", ")} }`;
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
function renderPromptMetadataLiteral(metadata: PromptCodegenMetadata): string {
|
|
1121
|
-
const properties = [
|
|
1122
|
-
`name: ${JSON.stringify(metadata.name)}`,
|
|
1123
|
-
renderOptionalStringProperty("description", metadata.description),
|
|
1124
|
-
renderOptionalStringProperty("version", metadata.version),
|
|
1125
|
-
renderOptionalStringProperty("role", metadata.role),
|
|
1126
|
-
renderOptionalStringProperty("goal", metadata.goal),
|
|
1127
|
-
`instructions: ${renderStringArray(metadata.instructions)}`,
|
|
1128
|
-
`constraints: ${renderStringArray(metadata.constraints)}`,
|
|
1129
|
-
`sections: ${renderPromptSections(metadata.sections)}`,
|
|
1130
|
-
].filter((line): line is string => line !== undefined);
|
|
1131
|
-
return `{ ${properties.join(", ")} }`;
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
function renderOptionalStringProperty(name: string, value: string | undefined): string | undefined {
|
|
1135
|
-
return value === undefined ? undefined : `${name}: ${JSON.stringify(value)}`;
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
function renderOptionalClassProperty(name: string, value: string | undefined): string | undefined {
|
|
1139
|
-
return value === undefined ? undefined : `${name}: ${value}`;
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
function renderOptionalNumberProperty(name: string, value: number | undefined): string | undefined {
|
|
1143
|
-
return value === undefined ? undefined : `${name}: ${value}`;
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
function renderClassArray(values: readonly string[]): string {
|
|
1147
|
-
return `Object.freeze([${values.join(", ")}])`;
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
function renderStringArray(values: readonly string[]): string {
|
|
1151
|
-
return `Object.freeze([${values.map((value) => JSON.stringify(value)).join(", ")}])`;
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
function renderPromptSections(values: readonly PromptSectionCodegenMetadata[]): string {
|
|
1155
|
-
return `Object.freeze([${values.map((value) => `Object.freeze(${renderPromptSectionLiteral(value)})`).join(", ")}])`;
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
function renderPromptSectionLiteral(value: PromptSectionCodegenMetadata): string {
|
|
1159
|
-
const properties = [
|
|
1160
|
-
`kind: ${JSON.stringify(value.kind)}`,
|
|
1161
|
-
renderOptionalStringProperty("title", value.title),
|
|
1162
|
-
`content: ${renderStringArray(value.content)}`,
|
|
1163
|
-
renderOptionalNumberProperty("priority", value.priority),
|
|
1164
|
-
].filter((line): line is string => line !== undefined);
|
|
1165
|
-
return `{ ${properties.join(", ")} }`;
|
|
1166
|
-
}
|