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
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type OpenApiCodegenOperationSpec,
|
|
8
8
|
type OpenApiCodegenSchema,
|
|
9
9
|
} from "../../library/openapi/codegen";
|
|
10
|
-
import {
|
|
10
|
+
import { collectToolSchemaModels, type ToolCodegenSource } from "./tool-codegen";
|
|
11
11
|
import {
|
|
12
12
|
analyzeRequestModelHydration,
|
|
13
13
|
type RequestModelHydration,
|
|
@@ -30,8 +30,6 @@ const FRAMEWORK_SOURCE = ((): string | null => {
|
|
|
30
30
|
try { return realpathSync("src/bazis") === FRAMEWORK_DIR ? "src/bazis" : null; } catch { return null; }
|
|
31
31
|
})();
|
|
32
32
|
const FRAMEWORK_ROOT = FRAMEWORK_SOURCE ?? "src/bazis";
|
|
33
|
-
const CORE_AGENT_CATALOG_FILE = `${FRAMEWORK_ROOT}/core/agent/generated/catalog.ts`;
|
|
34
|
-
const CORE_AGENT_GENERATED_DIR = `${FRAMEWORK_ROOT}/core/agent/generated`;
|
|
35
33
|
const isFrameworkSourcePath = (file: string): boolean => FRAMEWORK_SOURCE !== null && file.startsWith(`${FRAMEWORK_SOURCE}/`);
|
|
36
34
|
const APP_GENERATED_DIR = "src/generated/bazis";
|
|
37
35
|
let activeGeneratedDir = APP_GENERATED_DIR;
|
|
@@ -209,10 +207,10 @@ let ambiguousClassNames = new Set<string>();
|
|
|
209
207
|
// Exact roots that do not need convention name resolution: @RequestModel()
|
|
210
208
|
// classes used by generated transport schemas.
|
|
211
209
|
let explicitRequestModelRoots = new Set<ts.ClassDeclaration>();
|
|
212
|
-
let
|
|
210
|
+
let toolCodegenSources: ToolCodegenSource[] = [];
|
|
213
211
|
|
|
214
212
|
const ROUTE_DECORATORS = new Set(["Get", "Post", "Put", "Patch", "Delete", "Options", "Head", "All"]);
|
|
215
|
-
const CODEGEN_CHANNEL_DECORATORS = new Set(["Controller", "GrpcController", "WebSocketGateway", "
|
|
213
|
+
const CODEGEN_CHANNEL_DECORATORS = new Set(["Controller", "GrpcController", "WebSocketGateway", "Tool", "RequestModel", "UiProfile"]);
|
|
216
214
|
const PRIMITIVE_QUERY_TYPES: Record<string, HttpBindingSpec["type"]> = {
|
|
217
215
|
string: "string",
|
|
218
216
|
number: "number",
|
|
@@ -251,12 +249,7 @@ const BUILTIN_DEPENDENCY_NAMES = new Set([
|
|
|
251
249
|
function isScannableFile(filePath: string): boolean {
|
|
252
250
|
if (filePath.startsWith("../") || path.isAbsolute(filePath)) return false;
|
|
253
251
|
if (!filePath.endsWith(".ts") || filePath.endsWith(".d.ts") || /\.(test|spec)\.ts$/.test(filePath)) return false;
|
|
254
|
-
if (
|
|
255
|
-
filePath === CORE_AGENT_CATALOG_FILE ||
|
|
256
|
-
filePath.startsWith(`${APP_GENERATED_DIR}/`)
|
|
257
|
-
) {
|
|
258
|
-
return false;
|
|
259
|
-
}
|
|
252
|
+
if (filePath.startsWith(`${APP_GENERATED_DIR}/`)) return false;
|
|
260
253
|
return !/(^|\/)(test|tests|__tests__|fixture|fixtures|__fixtures__|generated|node_modules|vendor|dist|build|bin|coverage|examples)(\/|$)/.test(filePath) && !filePath.startsWith("admin-ui/");
|
|
261
254
|
}
|
|
262
255
|
|
|
@@ -414,7 +407,7 @@ function sourceHasCodegenCandidate(source: ts.SourceFile): boolean {
|
|
|
414
407
|
const decorators = ts.getDecorators(node) ?? [];
|
|
415
408
|
const constructorDeps = readConstructorTypeNames(node);
|
|
416
409
|
// Shared all-channel boundary index: any class that feeds DI/HTTP,
|
|
417
|
-
// request/list, OpenAPI/UI,
|
|
410
|
+
// request/list, OpenAPI/UI, Tool analysis must
|
|
418
411
|
// be owned by exactly one configured target before rendering begins.
|
|
419
412
|
if (
|
|
420
413
|
decorators.some((decorator) => {
|
|
@@ -456,7 +449,7 @@ function resetTargetAnalysis(files: Set<string>, generatedDir: string): void {
|
|
|
456
449
|
httpBindings = {}; httpControllerFiles = {}; openApiSchemas = {}; openApiOperations = {};
|
|
457
450
|
grpcBindings = new Map();
|
|
458
451
|
uiProfileResponseModels = new Map(); openApiSchemaModels = new Map(); requestModelClassNames = new Set(); listRequestClassNames = new Set();
|
|
459
|
-
classFilesByName = new Map(); classDeclarationsByName = new Map(); ambiguousClassNames = new Set(); explicitRequestModelRoots = new Set();
|
|
452
|
+
classFilesByName = new Map(); classDeclarationsByName = new Map(); ambiguousClassNames = new Set(); explicitRequestModelRoots = new Set(); toolCodegenSources = [];
|
|
460
453
|
sourceEntries = [...files].sort((a, b) => a.localeCompare(b)).map((filePath) => ({ filePath, source: programFiles.get(filePath) as ts.SourceFile }));
|
|
461
454
|
sourceFilePathSet = new Set(sourceEntries.map((entry) => entry.filePath));
|
|
462
455
|
openApiCodegen = createOpenApiCodegenAnalyzer({ checker, sourceFiles: sourceEntries.map((entry) => entry.source), schemaNameForDuplicate: duplicateOpenApiSchemaName });
|
|
@@ -478,7 +471,7 @@ async function generateTarget(name: string, reachable: Set<string>, production:
|
|
|
478
471
|
if (!FRAMEWORK_INTERNAL_PREFIXES.some((prefix) => filePath.startsWith(prefix))) {
|
|
479
472
|
collectClassDeps(source);
|
|
480
473
|
collectGrpcBindings(source);
|
|
481
|
-
|
|
474
|
+
toolCodegenSources.push({ filePath, sourceFile: source });
|
|
482
475
|
}
|
|
483
476
|
collectHttpBindings(source); collectOpenApiSchemas(source); collectUiProfileResponseModels(source);
|
|
484
477
|
}
|
|
@@ -487,46 +480,39 @@ async function generateTarget(name: string, reachable: Set<string>, production:
|
|
|
487
480
|
const requestModelImports = resolveRequestModelImports();
|
|
488
481
|
const requestModelHydration = resolveRequestModelHydration(requestModelImports);
|
|
489
482
|
const listModelImports = resolveListModelImports();
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
const schemaModels = resolveOpenApiSchemaModelImports(agent.schemaModels, agent.schemaNames);
|
|
494
|
-
classifyAgentWarnings(agent.warnings); failOnFatalErrors();
|
|
483
|
+
const tools = collectToolSchemaModels(toolCodegenSources, { schemaNameForDeclaration: openApiCodegen.schemaNameForDeclaration });
|
|
484
|
+
const schemaModels = resolveOpenApiSchemaModelImports(tools.schemaModels, tools.schemaNames);
|
|
485
|
+
classifyToolWarnings(tools.warnings); failOnFatalErrors();
|
|
495
486
|
const analysisMs = performance.now() - startedAt;
|
|
496
487
|
const renderStartedAt = performance.now();
|
|
497
488
|
const requestModels = renderRequestModels(requestModelImports, requestModelHydration);
|
|
498
489
|
const listModels = renderListModels(listModelImports);
|
|
499
|
-
const openApi = renderOpenApiMetadata(openApiSchemas, openApiOperations, "app",
|
|
490
|
+
const openApi = renderOpenApiMetadata(openApiSchemas, openApiOperations, "app", tools.schemaNames, schemaModels);
|
|
500
491
|
stageWrite(`${generatedDir}/deps.ts`, renderTargetDeps());
|
|
501
492
|
stageWrite(`${generatedDir}/bindings.ts`, renderTargetBindings());
|
|
502
493
|
stageWrite(`${generatedDir}/httpRequestModels.ts`, requestModels);
|
|
503
494
|
stageWrite(`${generatedDir}/httpListModels.ts`, listModels);
|
|
504
495
|
stageWrite(`${generatedDir}/openapi.ts`, openApi);
|
|
505
|
-
|
|
496
|
+
// Generated before 0.99.0, when agents were classes; nothing imports it now.
|
|
497
|
+
stageRemove(`${generatedDir}/agentCatalog.ts`);
|
|
506
498
|
stageWrite(`${generatedDir}/runtime.ts`, renderGeneratedRuntime(name));
|
|
507
499
|
if (production) stageWrite(`${generatedDir}/fingerprint.ts`, renderSourceFingerprint());
|
|
508
|
-
|
|
509
|
-
// is refreshed only when the framework source is part of this project.
|
|
510
|
-
if (production) {
|
|
511
|
-
if (FRAMEWORK_SOURCE !== null) {
|
|
512
|
-
stageWrite(CORE_AGENT_CATALOG_FILE, generateAgentMetadataCatalog([], CORE_AGENT_GENERATED_DIR).output);
|
|
513
|
-
}
|
|
514
|
-
} else {
|
|
500
|
+
if (!production) {
|
|
515
501
|
stageWrite(`${generatedDir}/bootstrap.ts`, 'import { registerBazisGeneratedRuntime } from "./runtime";\n\nawait registerBazisGeneratedRuntime();\n');
|
|
516
502
|
}
|
|
517
503
|
for (const warning of warnings) console.warn(`[di:generate] WARNING: ${warning}`);
|
|
518
|
-
console.log(`[di:generate] target=${name} sources=${sourceEntries.length} deps=${classDeps.size} controllers=${Object.keys(httpBindings).length} requestModels=${requestModelImports.length} listModels=${listModelImports.length}
|
|
504
|
+
console.log(`[di:generate] target=${name} sources=${sourceEntries.length} deps=${classDeps.size} controllers=${Object.keys(httpBindings).length} requestModels=${requestModelImports.length} listModels=${listModelImports.length} tools=${tools.toolCount} prepassMs=${prepassMs.toFixed(1)} analysisMs=${analysisMs.toFixed(1)} totalMs=${(performance.now() - startedAt).toFixed(1)}`);
|
|
519
505
|
return {
|
|
520
506
|
analysisMs,
|
|
521
507
|
renderMs: performance.now() - renderStartedAt,
|
|
522
508
|
eligible: sourceEntries.length,
|
|
523
|
-
candidates: classDeps.size + Object.keys(httpBindings).length + requestModelImports.length + listModelImports.length +
|
|
509
|
+
candidates: classDeps.size + Object.keys(httpBindings).length + requestModelImports.length + listModelImports.length + tools.toolCount,
|
|
524
510
|
};
|
|
525
511
|
}
|
|
526
512
|
|
|
527
|
-
function
|
|
528
|
-
for (let index = 0; index <
|
|
529
|
-
const warning =
|
|
513
|
+
function classifyToolWarnings(toolWarnings: readonly string[]): void {
|
|
514
|
+
for (let index = 0; index < toolWarnings.length; index += 1) {
|
|
515
|
+
const warning = toolWarnings[index] as string;
|
|
530
516
|
if (warning.includes("ambiguous") || warning.includes("declared in more than one file")) {
|
|
531
517
|
fatalErrors.push(warning);
|
|
532
518
|
} else {
|
|
@@ -561,8 +547,8 @@ function renderGeneratedRuntime(targetId: string): string {
|
|
|
561
547
|
"}",
|
|
562
548
|
"",
|
|
563
549
|
"async function loadTarget(): Promise<void> {",
|
|
564
|
-
" const [deps, bindings, requests, lists, openApi
|
|
565
|
-
` await registerBazisGeneratedTargetDescriptor({ id: ${JSON.stringify(targetId)}, classDeps: deps.GENERATED_TARGET_CLASS_DEPS, bindings: bindings.GENERATED_TARGET_BINDINGS, providerAttachments: bindings.GENERATED_PROVIDER_ATTACHMENTS, requestModels: requests.GENERATED_REQUEST_MODELS, requestShapes: requests.GENERATED_REQUEST_MODEL_SHAPES, listModels: lists.GENERATED_LIST_MODELS, openApi: openApi.GENERATED_OPENAPI_METADATA, openApiSchemaModels: openApi.GENERATED_OPENAPI_SCHEMA_MODELS
|
|
550
|
+
" const [deps, bindings, requests, lists, openApi] = await Promise.all([import(\"./deps\"), import(\"./bindings\"), import(\"./httpRequestModels\"), import(\"./httpListModels\"), import(\"./openapi\")]);",
|
|
551
|
+
` await registerBazisGeneratedTargetDescriptor({ id: ${JSON.stringify(targetId)}, classDeps: deps.GENERATED_TARGET_CLASS_DEPS, bindings: bindings.GENERATED_TARGET_BINDINGS, providerAttachments: bindings.GENERATED_PROVIDER_ATTACHMENTS, requestModels: requests.GENERATED_REQUEST_MODELS, requestShapes: requests.GENERATED_REQUEST_MODEL_SHAPES, listModels: lists.GENERATED_LIST_MODELS, openApi: openApi.GENERATED_OPENAPI_METADATA, openApiSchemaModels: openApi.GENERATED_OPENAPI_SCHEMA_MODELS });`,
|
|
566
552
|
"}",
|
|
567
553
|
"",
|
|
568
554
|
].join("\n");
|
|
@@ -582,8 +568,8 @@ function renderGeneratedRuntime(targetId: string): string {
|
|
|
582
568
|
" // Test-only owner-private hook: it is inert unless an isolated test installs it before loading this generated module.",
|
|
583
569
|
' (globalThis as { __bazisGeneratedRuntimeTestHook?: (target: string) => void }).__bazisGeneratedRuntimeTestHook?.("production");',
|
|
584
570
|
' const { registerBazisGeneratedTargetDescriptor, warnIfGeneratedSourcesChanged } = await import("bazis/core/generatedRuntime");',
|
|
585
|
-
" const [deps, bindings, requests, lists, openApi
|
|
586
|
-
` await registerBazisGeneratedTargetDescriptor({ id: ${JSON.stringify(targetId)}, classDeps: deps.GENERATED_TARGET_CLASS_DEPS, bindings: bindings.GENERATED_TARGET_BINDINGS, providerAttachments: bindings.GENERATED_PROVIDER_ATTACHMENTS, requestModels: requests.GENERATED_REQUEST_MODELS, requestShapes: requests.GENERATED_REQUEST_MODEL_SHAPES, listModels: lists.GENERATED_LIST_MODELS, openApi: openApi.GENERATED_OPENAPI_METADATA, openApiSchemaModels: openApi.GENERATED_OPENAPI_SCHEMA_MODELS
|
|
571
|
+
" const [deps, bindings, requests, lists, openApi] = await Promise.all([import(\"./deps\"), import(\"./bindings\"), import(\"./httpRequestModels\"), import(\"./httpListModels\"), import(\"./openapi\")]);",
|
|
572
|
+
` await registerBazisGeneratedTargetDescriptor({ id: ${JSON.stringify(targetId)}, classDeps: deps.GENERATED_TARGET_CLASS_DEPS, bindings: bindings.GENERATED_TARGET_BINDINGS, providerAttachments: bindings.GENERATED_PROVIDER_ATTACHMENTS, requestModels: requests.GENERATED_REQUEST_MODELS, requestShapes: requests.GENERATED_REQUEST_MODEL_SHAPES, listModels: lists.GENERATED_LIST_MODELS, openApi: openApi.GENERATED_OPENAPI_METADATA, openApiSchemaModels: openApi.GENERATED_OPENAPI_SCHEMA_MODELS });`,
|
|
587
573
|
" // Started from sources edited after codegen: say so instead of silently missing routes.",
|
|
588
574
|
' warnIfGeneratedSourcesChanged(import.meta.dir, (await import("./fingerprint")).GENERATED_SOURCE_FINGERPRINT);',
|
|
589
575
|
"}",
|
|
@@ -1348,11 +1334,11 @@ function resolveUiProfileResponseModelImports(): OpenApiSchemaModelImport[] {
|
|
|
1348
1334
|
|| sourcePathForDeclaration(left.declaration).localeCompare(sourcePathForDeclaration(right.declaration)));
|
|
1349
1335
|
}
|
|
1350
1336
|
|
|
1351
|
-
/**
|
|
1352
|
-
function resolveOpenApiSchemaModelImports(
|
|
1337
|
+
/** Tool contracts and their referenced classes share the existing nominal OpenAPI registry. */
|
|
1338
|
+
function resolveOpenApiSchemaModelImports(toolModels: readonly ts.ClassDeclaration[], toolNames: readonly string[]): OpenApiSchemaModelImport[] {
|
|
1353
1339
|
const models = new Map(resolveUiProfileResponseModelImports().map((model) => [model.declaration, model.schemaName]));
|
|
1354
1340
|
// Empty class contracts still have a real object schema and must survive filtering.
|
|
1355
|
-
for (const declaration of
|
|
1341
|
+
for (const declaration of toolModels) {
|
|
1356
1342
|
const name = openApiCodegen.schemaNameForDeclaration(declaration.name!.text, declaration);
|
|
1357
1343
|
openApiSchemas[name] ??= openApiCodegen.schemaFromDeclaration(declaration);
|
|
1358
1344
|
models.set(declaration, name);
|
|
@@ -1363,11 +1349,11 @@ function resolveOpenApiSchemaModelImports(agentModels: readonly ts.ClassDeclarat
|
|
|
1363
1349
|
reachable.add(name);
|
|
1364
1350
|
collectSchemaRefs(openApiSchemas[name], add);
|
|
1365
1351
|
};
|
|
1366
|
-
for (const name of
|
|
1352
|
+
for (const name of toolNames) add(name);
|
|
1367
1353
|
for (const [declaration, name] of openApiSchemaModels) {
|
|
1368
1354
|
if (!reachable.has(name)) continue;
|
|
1369
1355
|
if (!isNamedExportedTopLevelClass(declaration)) {
|
|
1370
|
-
fatalErrors.push(`
|
|
1356
|
+
fatalErrors.push(`BAZIS_TOOL_SCHEMA_MODEL_UNIMPORTABLE: Tool schema ${name} must be a named exported top-level class (${sourcePathForDeclaration(declaration)}).`);
|
|
1371
1357
|
continue;
|
|
1372
1358
|
}
|
|
1373
1359
|
openApiSchemas[name] ??= openApiCodegen.schemaFromDeclaration(declaration);
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
export interface ToolCodegenSource {
|
|
4
|
+
readonly filePath: string;
|
|
5
|
+
/** Program-owned AST; codegen never reparses application source. */
|
|
6
|
+
readonly sourceFile?: ts.SourceFile;
|
|
7
|
+
/** Test fixture input; project generation always supplies sourceFile. */
|
|
8
|
+
readonly content?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ToolCodegenResult {
|
|
12
|
+
readonly warnings: readonly string[];
|
|
13
|
+
readonly toolCount: number;
|
|
14
|
+
/** `input`/`output` classes of `@Tool` declarations: they get JSON schemas in the OpenAPI registry. */
|
|
15
|
+
readonly schemaNames: readonly string[];
|
|
16
|
+
/** Program-owned declarations of those classes, for nominal schema bindings. */
|
|
17
|
+
readonly schemaModels: readonly ts.ClassDeclaration[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ToolCodegenOptions {
|
|
21
|
+
/** Use the same naming policy as the project's OpenAPI analyzer. */
|
|
22
|
+
readonly schemaNameForDeclaration?: (name: string, declaration: ts.ClassDeclaration) => string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Finds `@Tool({ input, output })` classes. Tool metadata itself is read at
|
|
27
|
+
* runtime from the decorator; codegen only makes sure the contract classes
|
|
28
|
+
* have generated JSON schemas for the model and for validation.
|
|
29
|
+
*/
|
|
30
|
+
export function collectToolSchemaModels(
|
|
31
|
+
sources: readonly ToolCodegenSource[],
|
|
32
|
+
options: ToolCodegenOptions = {},
|
|
33
|
+
): ToolCodegenResult {
|
|
34
|
+
const warnings: string[] = [];
|
|
35
|
+
const declarations = new Map<string, ts.ClassDeclaration>();
|
|
36
|
+
const files = new Map<string, string>();
|
|
37
|
+
const ambiguous = new Set<string>();
|
|
38
|
+
const parsed = sources.map((source) =>
|
|
39
|
+
source.sourceFile ?? ts.createSourceFile(source.filePath, source.content ?? "", ts.ScriptTarget.ESNext, true, ts.ScriptKind.TS));
|
|
40
|
+
|
|
41
|
+
for (const source of parsed) {
|
|
42
|
+
visitClasses(source, (node) => {
|
|
43
|
+
const name = node.name!.text;
|
|
44
|
+
const existing = files.get(name);
|
|
45
|
+
if (existing === undefined) files.set(name, source.fileName);
|
|
46
|
+
else if (existing !== source.fileName) ambiguous.add(name);
|
|
47
|
+
declarations.set(name, node);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const contractNames = new Set<string>();
|
|
52
|
+
let toolCount = 0;
|
|
53
|
+
for (const source of parsed) {
|
|
54
|
+
visitClasses(source, (node) => {
|
|
55
|
+
const call = toolDecoratorCall(node);
|
|
56
|
+
if (call === undefined) return;
|
|
57
|
+
const className = node.name!.text;
|
|
58
|
+
const where = sourceLocation(source, node);
|
|
59
|
+
if (!isNamedExportedClass(node)) {
|
|
60
|
+
warnings.push(`@Tool class "${className}" (${where}) is not a named export; codegen can only import named-export classes.`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
toolCount += 1;
|
|
64
|
+
const options = call.arguments[0];
|
|
65
|
+
if (options === undefined || !ts.isObjectLiteralExpression(options)) return;
|
|
66
|
+
for (const property of options.properties) {
|
|
67
|
+
if (!ts.isPropertyAssignment(property)) continue;
|
|
68
|
+
const key = ts.isIdentifier(property.name) || ts.isStringLiteralLike(property.name) ? property.name.text : undefined;
|
|
69
|
+
if (key !== "input" && key !== "output") continue;
|
|
70
|
+
if (!ts.isIdentifier(property.initializer)) {
|
|
71
|
+
warnings.push(`@Tool ${className} (${where}): ${key} must be a class identifier.`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const contract = property.initializer.text;
|
|
75
|
+
if (ambiguous.has(contract)) {
|
|
76
|
+
warnings.push(`@Tool ${className} (${where}): ${key} class "${contract}" is declared in more than one file.`);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
contractNames.add(contract);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const schemaModels = [...contractNames]
|
|
85
|
+
.map((name) => declarations.get(name))
|
|
86
|
+
.filter((declaration): declaration is ts.ClassDeclaration => declaration !== undefined);
|
|
87
|
+
return {
|
|
88
|
+
warnings: Object.freeze(warnings),
|
|
89
|
+
toolCount,
|
|
90
|
+
schemaNames: Object.freeze(schemaModels
|
|
91
|
+
.map((declaration) => options.schemaNameForDeclaration?.(declaration.name!.text, declaration) ?? declaration.name!.text)
|
|
92
|
+
.sort((left, right) => left.localeCompare(right))),
|
|
93
|
+
schemaModels: Object.freeze(schemaModels),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function visitClasses(source: ts.SourceFile, onClass: (node: ts.ClassDeclaration) => void): void {
|
|
98
|
+
const visit = (node: ts.Node): void => {
|
|
99
|
+
if (ts.isClassDeclaration(node) && node.name) onClass(node);
|
|
100
|
+
ts.forEachChild(node, visit);
|
|
101
|
+
};
|
|
102
|
+
ts.forEachChild(source, visit);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function toolDecoratorCall(node: ts.ClassDeclaration): ts.CallExpression | undefined {
|
|
106
|
+
for (const decorator of ts.getDecorators(node) ?? []) {
|
|
107
|
+
const expression = decorator.expression;
|
|
108
|
+
if (ts.isCallExpression(expression) && ts.isIdentifier(expression.expression) && expression.expression.text === "Tool") return expression;
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function isNamedExportedClass(node: ts.ClassDeclaration): boolean {
|
|
114
|
+
const modifiers = ts.getModifiers(node) ?? [];
|
|
115
|
+
return modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)
|
|
116
|
+
&& !modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function sourceLocation(source: ts.SourceFile, node: ts.Node): string {
|
|
120
|
+
const position = source.getLineAndCharacterOfPosition(node.getStart(source));
|
|
121
|
+
return `${source.fileName}:${position.line + 1}:${position.character + 1}`;
|
|
122
|
+
}
|
package/index.ts
CHANGED
|
@@ -78,60 +78,44 @@ export {
|
|
|
78
78
|
} from "./core/di";
|
|
79
79
|
export {
|
|
80
80
|
AGENT_MODEL_PROVIDER,
|
|
81
|
-
|
|
82
|
-
AgentRegistry,
|
|
81
|
+
AGENT_RUNTIME_OPTIONS,
|
|
83
82
|
AgentRuntime,
|
|
84
83
|
AgentRuntimeError,
|
|
85
84
|
AgentSemanticError,
|
|
86
85
|
AgentToolExecutionError,
|
|
87
|
-
AgentToolExecutor,
|
|
88
86
|
ApproximateAgentContextTokenEstimator,
|
|
89
87
|
DefaultAgentContextBuilder,
|
|
90
|
-
Prompt,
|
|
91
|
-
Task,
|
|
92
88
|
Tool,
|
|
93
89
|
agentClassSchema,
|
|
94
90
|
agentData,
|
|
95
|
-
agentTaskMetadataOf,
|
|
96
91
|
agentInvocation,
|
|
97
92
|
agentJsonSchema,
|
|
98
93
|
agentMessage,
|
|
99
94
|
agentModelCapabilities,
|
|
100
95
|
agentModelRequest,
|
|
101
96
|
agentModelResponse,
|
|
102
|
-
agentOutput,
|
|
103
97
|
agentOutputContract,
|
|
104
98
|
agentText,
|
|
105
99
|
agentToolCall,
|
|
106
100
|
agentToolContract,
|
|
107
101
|
agentToolResult,
|
|
108
|
-
collectAgentCatalog,
|
|
109
|
-
type AgentCatalog,
|
|
110
|
-
type AgentCatalogExtras,
|
|
111
102
|
type AgentContext,
|
|
112
103
|
type AgentContextBuildInput,
|
|
113
104
|
type AgentContextBuilder,
|
|
114
105
|
type AgentContextLimits,
|
|
115
106
|
type AgentContextTokenEstimator,
|
|
116
107
|
type AgentContextTraceEntry,
|
|
117
|
-
type
|
|
108
|
+
type AgentDataDefinition,
|
|
118
109
|
type AgentInvocation,
|
|
119
110
|
type AgentMessage,
|
|
120
|
-
type AgentMetadataIndex,
|
|
121
|
-
type AgentTaskDefinition,
|
|
122
|
-
type AgentTaskMetadata,
|
|
123
|
-
type AgentTaskOptions,
|
|
124
|
-
type TaskDecoratorOptions,
|
|
125
111
|
type AgentModelCapabilities,
|
|
126
112
|
type AgentModelRequest,
|
|
127
113
|
type AgentModelResponse,
|
|
128
|
-
type AgentOptions,
|
|
129
114
|
type AgentOutputContract,
|
|
130
115
|
type AgentRuntimeInvokeOptions,
|
|
131
116
|
type AgentRuntimeOptions,
|
|
132
117
|
type AgentRuntimeResult,
|
|
133
118
|
type AgentRuntimeStatus,
|
|
134
|
-
type AgentRuntimeTaskInvokeOptions,
|
|
135
119
|
type AgentRuntimeToolExecutionOptions,
|
|
136
120
|
type AgentToolCall,
|
|
137
121
|
type AgentExecutableTool,
|
|
@@ -175,10 +159,7 @@ export {
|
|
|
175
159
|
type AgentModelProvider,
|
|
176
160
|
type AgentModelProviderContext,
|
|
177
161
|
type JsonValue,
|
|
178
|
-
type
|
|
179
|
-
type PromptSectionKind,
|
|
180
|
-
type PromptSectionMetadata,
|
|
181
|
-
type PromptSectionOptions,
|
|
162
|
+
type ToolDefinition,
|
|
182
163
|
type ToolOptions,
|
|
183
164
|
} from "./core/agent";
|
|
184
165
|
export {
|
|
@@ -461,12 +461,23 @@ export function createOpenApiCodegenAnalyzer(input: OpenApiCodegenAnalyzerInput)
|
|
|
461
461
|
};
|
|
462
462
|
|
|
463
463
|
const schemaFromArrayTsType = (type: ts.Type, seen: Set<string>): OpenApiCodegenSchema => {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
464
|
+
// A recursive alias can close its loop through an array without an object
|
|
465
|
+
// in between (`type Json = … | readonly Json[]`): the repeat is an untyped array.
|
|
466
|
+
const key = typeIdentity(type);
|
|
467
|
+
if (seen.has(key)) {
|
|
468
|
+
return { type: "array" };
|
|
469
|
+
}
|
|
470
|
+
seen.add(key);
|
|
471
|
+
try {
|
|
472
|
+
if (checker.isTupleType(type)) {
|
|
473
|
+
const tupleArgs = typeArgumentsOf(type);
|
|
474
|
+
return { type: "array", items: mergeResponseSchemas(tupleArgs.map((item) => schemaFromTsType(item, seen))) ?? {} };
|
|
475
|
+
}
|
|
476
|
+
const args = typeArgumentsOf(type);
|
|
477
|
+
return { type: "array", items: args[0] ? schemaFromTsType(args[0], seen) : {} };
|
|
478
|
+
} finally {
|
|
479
|
+
seen.delete(key);
|
|
467
480
|
}
|
|
468
|
-
const args = typeArgumentsOf(type);
|
|
469
|
-
return { type: "array", items: args[0] ? schemaFromTsType(args[0], seen) : {} };
|
|
470
481
|
};
|
|
471
482
|
|
|
472
483
|
const schemaFromObjectTsType = (type: ts.Type, seen: Set<string>): OpenApiCodegenSchema => {
|
package/package.json
CHANGED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { defineAgentMetadata, type AgentOptions } from "../metadata";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Declares an executable agent class. The class stays a regular DI service;
|
|
5
|
-
* this decorator only adds framework metadata for discovery and validation.
|
|
6
|
-
*/
|
|
7
|
-
export function Agent(options: AgentOptions = {}) {
|
|
8
|
-
return (value: abstract new (...args: never[]) => unknown, context: ClassDecoratorContext): void => {
|
|
9
|
-
defineAgentMetadata(value, context.metadata, options);
|
|
10
|
-
};
|
|
11
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { definePromptMetadata, type PromptOptions } from "../metadata";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Declares a reusable prompt contract. Prompts are metadata classes so modules
|
|
5
|
-
* can own and version them without exposing provider-specific APIs.
|
|
6
|
-
*/
|
|
7
|
-
export function Prompt(options: PromptOptions = {}) {
|
|
8
|
-
return (value: abstract new (...args: never[]) => unknown, context: ClassDecoratorContext): void => {
|
|
9
|
-
definePromptMetadata(value, context.metadata, options);
|
|
10
|
-
};
|
|
11
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { defineAgentTaskMetadata, type AgentTaskOptions } from "../metadata";
|
|
2
|
-
|
|
3
|
-
type TaskDecorator = (value: (...args: never[]) => unknown, context: ClassMethodDecoratorContext) => void;
|
|
4
|
-
|
|
5
|
-
export type TaskDecoratorOptions = string | AgentTaskOptions;
|
|
6
|
-
|
|
7
|
-
function normalizeOptions(options: TaskDecoratorOptions | undefined): AgentTaskOptions {
|
|
8
|
-
if (options === undefined) {
|
|
9
|
-
return {};
|
|
10
|
-
}
|
|
11
|
-
if (typeof options === "string") {
|
|
12
|
-
return { name: options };
|
|
13
|
-
}
|
|
14
|
-
return options;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Declares an executable agent task on an @Agent class. This mirrors
|
|
19
|
-
* controller action decorators: the method is a business operation contract,
|
|
20
|
-
* while AgentRuntime performs the actual AI execution.
|
|
21
|
-
*/
|
|
22
|
-
export function Task(options?: TaskDecoratorOptions): TaskDecorator {
|
|
23
|
-
return (_value, context) => {
|
|
24
|
-
if (context.static || context.private) {
|
|
25
|
-
throw new Error(`@Task supports public instance methods only ("${String(context.name)}").`);
|
|
26
|
-
}
|
|
27
|
-
defineAgentTaskMetadata(context.name, context.metadata, normalizeOptions(options));
|
|
28
|
-
};
|
|
29
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
// This file is auto-generated by `bazis codegen`.
|
|
2
|
-
// Do not edit manually.
|
|
3
|
-
|
|
4
|
-
import type { Class } from "../../di";
|
|
5
|
-
import type { AgentMetadataIndex } from "../AgentRegistry";
|
|
6
|
-
import type { AgentMetadata, PromptMetadata, ToolMetadata } from "../metadata";
|
|
7
|
-
|
|
8
|
-
function readonlyMetadataMap<K, V>(entries: readonly (readonly [K, V])[]): ReadonlyMap<K, V> {
|
|
9
|
-
const lookup = new Map<K, V>(entries);
|
|
10
|
-
const facade: ReadonlyMap<K, V> = {
|
|
11
|
-
get size() { return lookup.size; }, has: (key) => lookup.has(key), get: (key) => lookup.get(key),
|
|
12
|
-
entries: () => lookup.entries(), keys: () => lookup.keys(), values: () => lookup.values(),
|
|
13
|
-
forEach: (callback, thisArg) => { for (const [key, value] of lookup) callback.call(thisArg, value, key, facade); },
|
|
14
|
-
[Symbol.iterator]: () => lookup[Symbol.iterator](),
|
|
15
|
-
};
|
|
16
|
-
return Object.freeze(facade);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const GENERATED_AGENT_METADATA: AgentMetadataIndex = Object.freeze({
|
|
20
|
-
agents: readonlyMetadataMap<Class<object>, AgentMetadata>([
|
|
21
|
-
]),
|
|
22
|
-
tools: readonlyMetadataMap<Class<object>, ToolMetadata>([
|
|
23
|
-
]),
|
|
24
|
-
prompts: readonlyMetadataMap<Class<object>, PromptMetadata>([
|
|
25
|
-
]),
|
|
26
|
-
});
|