agency-lang 0.0.99 → 0.0.101
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/dist/lib/backends/typescriptBuilder.d.ts +5 -1
- package/dist/lib/backends/typescriptBuilder.js +24 -7
- package/dist/lib/backends/typescriptGenerator/builtins.js +3 -0
- package/dist/lib/backends/typescriptGenerator.d.ts +1 -1
- package/dist/lib/backends/typescriptGenerator.js +2 -2
- package/dist/lib/cli/commands.js +19 -2
- package/dist/lib/cli/debug.js +8 -1
- package/dist/lib/config.d.ts +64 -0
- package/dist/lib/config.js +44 -0
- package/dist/lib/config.test.d.ts +1 -0
- package/dist/lib/config.test.js +64 -0
- package/dist/lib/debugger/uiState.js +4 -3
- package/dist/lib/importPaths.d.ts +6 -2
- package/dist/lib/importPaths.js +14 -3
- package/dist/lib/importStrategy.d.ts +3 -0
- package/dist/lib/importStrategy.js +40 -15
- package/dist/lib/runtime/index.d.ts +2 -0
- package/dist/lib/runtime/index.js +1 -0
- package/dist/lib/runtime/mcp/__tests__/testServer.d.ts +1 -0
- package/dist/lib/runtime/mcp/__tests__/testServer.js +14 -0
- package/dist/lib/runtime/mcp/mcp.integration.test.d.ts +1 -0
- package/dist/lib/runtime/mcp/mcp.integration.test.js +37 -0
- package/dist/lib/runtime/mcp/mcpConnection.d.ts +14 -0
- package/dist/lib/runtime/mcp/mcpConnection.js +61 -0
- package/dist/lib/runtime/mcp/mcpConnection.test.d.ts +1 -0
- package/dist/lib/runtime/mcp/mcpConnection.test.js +35 -0
- package/dist/lib/runtime/mcp/mcpManager.d.ts +11 -0
- package/dist/lib/runtime/mcp/mcpManager.js +51 -0
- package/dist/lib/runtime/mcp/mcpManager.test.d.ts +1 -0
- package/dist/lib/runtime/mcp/mcpManager.test.js +70 -0
- package/dist/lib/runtime/mcp/toolAdapter.d.ts +6 -0
- package/dist/lib/runtime/mcp/toolAdapter.js +46 -0
- package/dist/lib/runtime/mcp/toolAdapter.test.d.ts +1 -0
- package/dist/lib/runtime/mcp/toolAdapter.test.js +68 -0
- package/dist/lib/runtime/mcp/types.d.ts +18 -0
- package/dist/lib/runtime/mcp/types.js +1 -0
- package/dist/lib/runtime/node.js +1 -0
- package/dist/lib/runtime/prompt.js +9 -1
- package/dist/lib/runtime/state/context.d.ts +5 -0
- package/dist/lib/runtime/state/context.js +13 -0
- package/dist/lib/templates/backends/typescriptGenerator/builtinFunctions/mcp.d.ts +4 -0
- package/dist/lib/templates/backends/typescriptGenerator/builtinFunctions/mcp.js +11 -0
- package/dist/lib/templates/backends/typescriptGenerator/imports.d.ts +1 -1
- package/dist/lib/templates/backends/typescriptGenerator/imports.js +1 -1
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.js +1 -1
- package/package.json +2 -1
- package/stdlib/_builtins.js +134 -0
- package/stdlib/_math.js +9 -0
- package/stdlib/_utils.js +51 -0
- package/stdlib/agent.js +4 -1
- package/stdlib/array.js +1732 -1812
- package/stdlib/clipboard.js +4 -1
- package/stdlib/fs.js +4 -1
- package/stdlib/http.js +4 -1
- package/stdlib/index.js +1860 -1875
- package/stdlib/math.agency +3 -5
- package/stdlib/math.js +8 -9
- package/stdlib/object.js +1160 -836
- package/stdlib/path.js +4 -1
- package/stdlib/shell.js +4 -1
- package/stdlib/speech.js +4 -1
- package/stdlib/strategy.js +4 -1
- package/stdlib/system.js +4 -1
- package/stdlib/ui.js +4 -1
- package/stdlib/weather.js +4 -1
- package/stdlib/wikipedia.js +4 -1
|
@@ -29,14 +29,18 @@ export declare class TypeScriptBuilder {
|
|
|
29
29
|
private _sourceMapBuilder;
|
|
30
30
|
private programInfo;
|
|
31
31
|
private moduleId;
|
|
32
|
+
private outputFile;
|
|
32
33
|
/**
|
|
33
34
|
* @param config - Agency compiler configuration (model defaults, logging, etc.)
|
|
34
35
|
* @param info - Pre-collected program metadata (function definitions, graph nodes, imports, type hints)
|
|
35
36
|
* @param moduleId - Unique identifier for this module (e.g., "foo.agency"), used to
|
|
36
37
|
* namespace global variables in the GlobalStore so that different modules' globals
|
|
37
38
|
* don't collide. Must be consistent between the defining module and any importers.
|
|
39
|
+
* @param outputFile - Absolute path where the generated code will be written.
|
|
40
|
+
* Used to compute relative import paths for stdlib. If not provided, falls
|
|
41
|
+
* back to resolving moduleId against cwd.
|
|
38
42
|
*/
|
|
39
|
-
constructor(config: AgencyConfig | undefined, info: ProgramInfo, moduleId: string);
|
|
43
|
+
constructor(config: AgencyConfig | undefined, info: ProgramInfo, moduleId: string, outputFile?: string);
|
|
40
44
|
private configDefaults;
|
|
41
45
|
/** Convert a TsNode to string (for use in template-based methods) */
|
|
42
46
|
private str;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from "path";
|
|
1
2
|
import { formatTypeHint } from "../cli/util.js";
|
|
2
3
|
import { BUILTIN_FUNCTIONS, BUILTIN_TOOLS, BUILTIN_VARIABLES, TYPES_THAT_DONT_TRIGGER_NEW_PART, } from "../config.js";
|
|
3
4
|
import { expressionToString } from "../utils/node.js";
|
|
@@ -75,17 +76,22 @@ export class TypeScriptBuilder {
|
|
|
75
76
|
_sourceMapBuilder = new SourceMapBuilder();
|
|
76
77
|
programInfo;
|
|
77
78
|
moduleId;
|
|
79
|
+
outputFile;
|
|
78
80
|
/**
|
|
79
81
|
* @param config - Agency compiler configuration (model defaults, logging, etc.)
|
|
80
82
|
* @param info - Pre-collected program metadata (function definitions, graph nodes, imports, type hints)
|
|
81
83
|
* @param moduleId - Unique identifier for this module (e.g., "foo.agency"), used to
|
|
82
84
|
* namespace global variables in the GlobalStore so that different modules' globals
|
|
83
85
|
* don't collide. Must be consistent between the defining module and any importers.
|
|
86
|
+
* @param outputFile - Absolute path where the generated code will be written.
|
|
87
|
+
* Used to compute relative import paths for stdlib. If not provided, falls
|
|
88
|
+
* back to resolving moduleId against cwd.
|
|
84
89
|
*/
|
|
85
|
-
constructor(config, info, moduleId) {
|
|
90
|
+
constructor(config, info, moduleId, outputFile) {
|
|
86
91
|
this.agencyConfig = mergeDeep(this.configDefaults(), config || {});
|
|
87
92
|
this.programInfo = info;
|
|
88
93
|
this.moduleId = moduleId;
|
|
94
|
+
this.outputFile = outputFile;
|
|
89
95
|
}
|
|
90
96
|
configDefaults() {
|
|
91
97
|
return {
|
|
@@ -1041,7 +1047,7 @@ export class TypeScriptBuilder {
|
|
|
1041
1047
|
return ts.runnerIfElse({ id, branches, elseBranch });
|
|
1042
1048
|
}
|
|
1043
1049
|
processImportStatement(node) {
|
|
1044
|
-
const from = toCompiledImportPath(node.modulePath);
|
|
1050
|
+
const from = toCompiledImportPath(node.modulePath, this.outputFile ?? path.resolve(this.moduleId));
|
|
1045
1051
|
const imports = node.importedNames.map((nameType) => {
|
|
1046
1052
|
switch (nameType.type) {
|
|
1047
1053
|
case "namedImport":
|
|
@@ -1092,7 +1098,7 @@ export class TypeScriptBuilder {
|
|
|
1092
1098
|
return ts.importDecl({
|
|
1093
1099
|
importKind: "named",
|
|
1094
1100
|
names: importNames,
|
|
1095
|
-
from: toCompiledImportPath(node.agencyFile),
|
|
1101
|
+
from: toCompiledImportPath(node.agencyFile, this.outputFile ?? path.resolve(this.moduleId)),
|
|
1096
1102
|
});
|
|
1097
1103
|
}
|
|
1098
1104
|
// ------- TsRaw wrapper methods (template-heavy) -------
|
|
@@ -1862,6 +1868,7 @@ export class TypeScriptBuilder {
|
|
|
1862
1868
|
const configArg = node.arguments[1];
|
|
1863
1869
|
let clientConfig;
|
|
1864
1870
|
let configToolNames = [];
|
|
1871
|
+
let configToolExprs = [];
|
|
1865
1872
|
if (configArg && configArg.type === "agencyObject") {
|
|
1866
1873
|
// Extract tools from config object
|
|
1867
1874
|
const toolsEntry = configArg.entries.find((e) => !("type" in e && e.type === "splat") &&
|
|
@@ -1881,6 +1888,10 @@ export class TypeScriptBuilder {
|
|
|
1881
1888
|
configToolNames.push(toolArg.value);
|
|
1882
1889
|
}
|
|
1883
1890
|
}
|
|
1891
|
+
else if (item.type === "splat") {
|
|
1892
|
+
// Pass-through: spread MCP tool arrays (or any other spread) directly
|
|
1893
|
+
configToolExprs.push(ts.spread(this.processNode(item.value)));
|
|
1894
|
+
}
|
|
1884
1895
|
}
|
|
1885
1896
|
}
|
|
1886
1897
|
// Build clientConfig without known keys
|
|
@@ -1912,12 +1923,14 @@ export class TypeScriptBuilder {
|
|
|
1912
1923
|
const toolNodes = allToolNames.map((name) => $(ts.id("tool"))
|
|
1913
1924
|
.call([ts.str(name)])
|
|
1914
1925
|
.done());
|
|
1926
|
+
// Merge registry-resolved tools with pass-through expressions (spreads of MCP tools, etc.)
|
|
1927
|
+
const allToolNodes = [...toolNodes, ...configToolExprs];
|
|
1915
1928
|
// Merge tools into clientConfig
|
|
1916
1929
|
let mergedConfig;
|
|
1917
|
-
if (
|
|
1930
|
+
if (allToolNodes.length > 0) {
|
|
1918
1931
|
// Spread user config and add tools
|
|
1919
1932
|
mergedConfig = ts.obj([
|
|
1920
|
-
ts.set("tools", ts.arr(
|
|
1933
|
+
ts.set("tools", ts.arr(allToolNodes)),
|
|
1921
1934
|
ts.setSpread(clientConfig),
|
|
1922
1935
|
]);
|
|
1923
1936
|
}
|
|
@@ -2418,10 +2431,14 @@ export class TypeScriptBuilder {
|
|
|
2418
2431
|
traceConfigFields.traceFile = ts.str(this.agencyConfig.traceFile);
|
|
2419
2432
|
}
|
|
2420
2433
|
runtimeCtxArgs.traceConfig = ts.obj(traceConfigFields);
|
|
2421
|
-
|
|
2434
|
+
const runtimeCtxStatements = [
|
|
2422
2435
|
ts.constDecl("__globalCtx", ts.new(ts.id("RuntimeContext"), [ts.obj(runtimeCtxArgs)])),
|
|
2423
2436
|
ts.constDecl("graph", $(ts.runtime.globalCtx).prop("graph").done()),
|
|
2424
|
-
]
|
|
2437
|
+
];
|
|
2438
|
+
if (this.agencyConfig.mcpServers) {
|
|
2439
|
+
runtimeCtxStatements.push(ts.raw(`__globalCtx.createMcpManager(${JSON.stringify(this.agencyConfig.mcpServers)});`));
|
|
2440
|
+
}
|
|
2441
|
+
let runtimeCtx = ts.statements(runtimeCtxStatements);
|
|
2425
2442
|
return renderImports.default({
|
|
2426
2443
|
runtimeContextCode: printTs(runtimeCtx),
|
|
2427
2444
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as builtinFunctionsInput from "../../templates/backends/typescriptGenerator/builtinFunctions/input.js";
|
|
2
2
|
import * as builtinFunctionsRead from "../../templates/backends/typescriptGenerator/builtinFunctions/read.js";
|
|
3
3
|
import * as builtinFunctionsFetchJSON from "../../templates/backends/typescriptGenerator/builtinFunctions/fetchJSON.js";
|
|
4
|
+
import * as builtinFunctionsMcp from "../../templates/backends/typescriptGenerator/builtinFunctions/mcp.js";
|
|
4
5
|
import { BUILTIN_FUNCTIONS } from "../../config.js";
|
|
5
6
|
/**
|
|
6
7
|
* Maps an Agency function name to its TypeScript equivalent
|
|
@@ -46,5 +47,7 @@ export function generateBuiltinHelpers(functionsUsed) {
|
|
|
46
47
|
helpers.push(sleepFunc);
|
|
47
48
|
}
|
|
48
49
|
*/
|
|
50
|
+
const mcpFunc = builtinFunctionsMcp.default({});
|
|
51
|
+
helpers.push(mcpFunc);
|
|
49
52
|
return helpers.join("\n\n");
|
|
50
53
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { AgencyProgram } from "../types.js";
|
|
2
2
|
import { type ProgramInfo } from "../programInfo.js";
|
|
3
3
|
import { AgencyConfig } from "../config.js";
|
|
4
|
-
export declare function generateTypeScript(program: AgencyProgram, config?: AgencyConfig, info?: ProgramInfo, moduleId?: string): string;
|
|
4
|
+
export declare function generateTypeScript(program: AgencyProgram, config?: AgencyConfig, info?: ProgramInfo, moduleId?: string, outputFile?: string): string;
|
|
@@ -2,14 +2,14 @@ import { TypescriptPreprocessor } from "../preprocessors/typescriptPreprocessor.
|
|
|
2
2
|
import { collectProgramInfo } from "../programInfo.js";
|
|
3
3
|
import { TypeScriptBuilder } from "./typescriptBuilder.js";
|
|
4
4
|
import { printTs } from "../ir/prettyPrint.js";
|
|
5
|
-
export function generateTypeScript(program, config, info, moduleId) {
|
|
5
|
+
export function generateTypeScript(program, config, info, moduleId, outputFile) {
|
|
6
6
|
if (!moduleId) {
|
|
7
7
|
throw new Error("moduleId is required for generateTypeScript");
|
|
8
8
|
}
|
|
9
9
|
const programInfo = info ?? collectProgramInfo(program);
|
|
10
10
|
const preprocessor = new TypescriptPreprocessor(program, config, programInfo);
|
|
11
11
|
const preprocessedProgram = preprocessor.preprocess();
|
|
12
|
-
const builder = new TypeScriptBuilder(config, programInfo, moduleId);
|
|
12
|
+
const builder = new TypeScriptBuilder(config, programInfo, moduleId, outputFile);
|
|
13
13
|
const ir = builder.build(preprocessedProgram);
|
|
14
14
|
return printTs(ir);
|
|
15
15
|
}
|
package/dist/lib/cli/commands.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { generateAgency } from "../backends/agencyGenerator.js";
|
|
2
|
+
import { AgencyConfigSchema } from "../config.js";
|
|
2
3
|
import { generateTypeScript } from "../index.js";
|
|
3
4
|
import { resolveImports } from "../preprocessors/importResolver.js";
|
|
4
5
|
import { collectProgramInfo } from "../programInfo.js";
|
|
@@ -26,6 +27,15 @@ export function loadConfig(configPath, verbose = false) {
|
|
|
26
27
|
try {
|
|
27
28
|
const configContent = fs.readFileSync(finalConfigPath, "utf-8");
|
|
28
29
|
config = JSON.parse(configContent);
|
|
30
|
+
const parseResult = AgencyConfigSchema.safeParse(config);
|
|
31
|
+
if (!parseResult.success) {
|
|
32
|
+
console.error(`Invalid agency.json config:`);
|
|
33
|
+
for (const issue of parseResult.error.issues) {
|
|
34
|
+
console.error(` - ${issue.path.join(".")}: ${issue.message}`);
|
|
35
|
+
}
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
config = parseResult.data;
|
|
29
39
|
if (config.verbose) {
|
|
30
40
|
console.log(`Loaded config from ${finalConfigPath}`);
|
|
31
41
|
}
|
|
@@ -154,9 +164,16 @@ export function compile(config, inputFile, _outputFile, options) {
|
|
|
154
164
|
nonAgencyImports.push(node.modulePath);
|
|
155
165
|
}
|
|
156
166
|
});
|
|
157
|
-
|
|
167
|
+
try {
|
|
168
|
+
strategy.prepareDependencies(nonAgencyImports, absoluteInputFile);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
158
174
|
const moduleId = path.relative(process.cwd(), absoluteInputFile);
|
|
159
|
-
const
|
|
175
|
+
const absoluteOutputFile = path.resolve(outputFile);
|
|
176
|
+
const generatedCode = generateTypeScript(resolvedProgram, config, info, moduleId, absoluteOutputFile);
|
|
160
177
|
if (options?.ts) {
|
|
161
178
|
// TypeScript output — add @ts-nocheck so type errors don't block compilation
|
|
162
179
|
fs.writeFileSync(outputFile, "// @ts-nocheck\n" + generatedCode, "utf-8");
|
package/dist/lib/cli/debug.js
CHANGED
|
@@ -83,7 +83,14 @@ export async function debug(config, _inputFile, options = {}) {
|
|
|
83
83
|
let absOutput;
|
|
84
84
|
if (distDir) {
|
|
85
85
|
// distDir mode: import pre-compiled JS from the dist directory
|
|
86
|
-
|
|
86
|
+
let compiledPath;
|
|
87
|
+
try {
|
|
88
|
+
compiledPath = resolveCompiledFile(distDir, inputFile);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
87
94
|
// Warn if source is newer than compiled output
|
|
88
95
|
const sourceMtime = fs.statSync(inputFile).mtimeMs;
|
|
89
96
|
const compiledMtime = fs.statSync(compiledPath).mtimeMs;
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { AgencyNode } from "./types.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { McpServerConfig } from "./runtime/mcp/types.js";
|
|
2
4
|
export declare const TYPES_THAT_DONT_TRIGGER_NEW_PART: AgencyNode["type"][];
|
|
3
5
|
/**
|
|
4
6
|
* Maps Agency built-in function names to TypeScript equivalents.
|
|
@@ -117,4 +119,66 @@ export interface AgencyConfig {
|
|
|
117
119
|
/** Base URL for source links in generated docs */
|
|
118
120
|
baseUrl?: string;
|
|
119
121
|
};
|
|
122
|
+
/** MCP server configurations */
|
|
123
|
+
mcpServers?: Record<string, McpServerConfig>;
|
|
120
124
|
}
|
|
125
|
+
export declare const AgencyConfigSchema: z.ZodObject<{
|
|
126
|
+
verbose: z.ZodOptional<z.ZodBoolean>;
|
|
127
|
+
outDir: z.ZodOptional<z.ZodString>;
|
|
128
|
+
excludeNodeTypes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
129
|
+
excludeBuiltinFunctions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
130
|
+
allowedFetchDomains: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
131
|
+
disallowedFetchDomains: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
132
|
+
tarsecTraceHost: z.ZodOptional<z.ZodString>;
|
|
133
|
+
maxToolCallRounds: z.ZodOptional<z.ZodNumber>;
|
|
134
|
+
log: z.ZodOptional<z.ZodObject<{
|
|
135
|
+
host: z.ZodOptional<z.ZodString>;
|
|
136
|
+
projectId: z.ZodOptional<z.ZodString>;
|
|
137
|
+
debugMode: z.ZodOptional<z.ZodBoolean>;
|
|
138
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
139
|
+
}, z.core.$strip>>;
|
|
140
|
+
client: z.ZodOptional<z.ZodObject<{
|
|
141
|
+
logLevel: z.ZodOptional<z.ZodEnum<{
|
|
142
|
+
error: "error";
|
|
143
|
+
warn: "warn";
|
|
144
|
+
info: "info";
|
|
145
|
+
debug: "debug";
|
|
146
|
+
}>>;
|
|
147
|
+
defaultModel: z.ZodOptional<z.ZodString>;
|
|
148
|
+
openAiApiKey: z.ZodOptional<z.ZodString>;
|
|
149
|
+
googleApiKey: z.ZodOptional<z.ZodString>;
|
|
150
|
+
statelog: z.ZodOptional<z.ZodObject<{
|
|
151
|
+
host: z.ZodOptional<z.ZodString>;
|
|
152
|
+
projectId: z.ZodOptional<z.ZodString>;
|
|
153
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
154
|
+
}, z.core.$strip>>;
|
|
155
|
+
}, z.core.$strip>>;
|
|
156
|
+
strictTypes: z.ZodOptional<z.ZodBoolean>;
|
|
157
|
+
typeCheck: z.ZodOptional<z.ZodBoolean>;
|
|
158
|
+
typeCheckStrict: z.ZodOptional<z.ZodBoolean>;
|
|
159
|
+
restrictImports: z.ZodOptional<z.ZodBoolean>;
|
|
160
|
+
debugger: z.ZodOptional<z.ZodBoolean>;
|
|
161
|
+
instrument: z.ZodOptional<z.ZodBoolean>;
|
|
162
|
+
checkpoints: z.ZodOptional<z.ZodObject<{
|
|
163
|
+
maxRestores: z.ZodOptional<z.ZodNumber>;
|
|
164
|
+
}, z.core.$strip>>;
|
|
165
|
+
trace: z.ZodOptional<z.ZodBoolean>;
|
|
166
|
+
traceFile: z.ZodOptional<z.ZodString>;
|
|
167
|
+
traceDir: z.ZodOptional<z.ZodString>;
|
|
168
|
+
distDir: z.ZodOptional<z.ZodString>;
|
|
169
|
+
test: z.ZodOptional<z.ZodObject<{
|
|
170
|
+
parallel: z.ZodOptional<z.ZodNumber>;
|
|
171
|
+
}, z.core.$strip>>;
|
|
172
|
+
doc: z.ZodOptional<z.ZodObject<{
|
|
173
|
+
outDir: z.ZodOptional<z.ZodString>;
|
|
174
|
+
baseUrl: z.ZodOptional<z.ZodString>;
|
|
175
|
+
}, z.core.$strip>>;
|
|
176
|
+
mcpServers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
|
|
177
|
+
command: z.ZodString;
|
|
178
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
179
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
180
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
181
|
+
type: z.ZodLiteral<"http">;
|
|
182
|
+
url: z.ZodString;
|
|
183
|
+
}, z.core.$strict>]>>>;
|
|
184
|
+
}, z.core.$loose>;
|
package/dist/lib/config.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
1
2
|
export const TYPES_THAT_DONT_TRIGGER_NEW_PART = [
|
|
2
3
|
"typeAlias",
|
|
3
4
|
"usesTool",
|
|
@@ -16,3 +17,46 @@ export const BUILTIN_TOOLS = [
|
|
|
16
17
|
"readSkill",
|
|
17
18
|
];
|
|
18
19
|
export const BUILTIN_VARIABLES = ["color"];
|
|
20
|
+
// --- Zod schema for runtime validation of agency.json ---
|
|
21
|
+
const McpStdioServerSchema = z.object({
|
|
22
|
+
command: z.string(),
|
|
23
|
+
args: z.array(z.string()).optional(),
|
|
24
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
25
|
+
}).strict();
|
|
26
|
+
const McpHttpServerSchema = z.object({
|
|
27
|
+
type: z.literal("http"),
|
|
28
|
+
url: z.string(),
|
|
29
|
+
}).strict();
|
|
30
|
+
const McpServerSchema = z.union([McpStdioServerSchema, McpHttpServerSchema]);
|
|
31
|
+
export const AgencyConfigSchema = z.object({
|
|
32
|
+
verbose: z.boolean(),
|
|
33
|
+
outDir: z.string(),
|
|
34
|
+
excludeNodeTypes: z.array(z.string()),
|
|
35
|
+
excludeBuiltinFunctions: z.array(z.string()),
|
|
36
|
+
allowedFetchDomains: z.array(z.string()),
|
|
37
|
+
disallowedFetchDomains: z.array(z.string()),
|
|
38
|
+
tarsecTraceHost: z.string(),
|
|
39
|
+
maxToolCallRounds: z.number(),
|
|
40
|
+
log: z.object({ host: z.string(), projectId: z.string(), debugMode: z.boolean(), apiKey: z.string() }).partial(),
|
|
41
|
+
client: z.object({
|
|
42
|
+
logLevel: z.enum(["error", "warn", "info", "debug"]),
|
|
43
|
+
defaultModel: z.string(),
|
|
44
|
+
openAiApiKey: z.string(),
|
|
45
|
+
googleApiKey: z.string(),
|
|
46
|
+
statelog: z.object({ host: z.string(), projectId: z.string(), apiKey: z.string() }).partial(),
|
|
47
|
+
}).partial(),
|
|
48
|
+
strictTypes: z.boolean(),
|
|
49
|
+
typeCheck: z.boolean(),
|
|
50
|
+
typeCheckStrict: z.boolean(),
|
|
51
|
+
restrictImports: z.boolean(),
|
|
52
|
+
debugger: z.boolean(),
|
|
53
|
+
instrument: z.boolean(),
|
|
54
|
+
checkpoints: z.object({ maxRestores: z.number() }).partial(),
|
|
55
|
+
trace: z.boolean(),
|
|
56
|
+
traceFile: z.string(),
|
|
57
|
+
traceDir: z.string(),
|
|
58
|
+
distDir: z.string(),
|
|
59
|
+
test: z.object({ parallel: z.number() }).partial(),
|
|
60
|
+
doc: z.object({ outDir: z.string(), baseUrl: z.string() }).partial(),
|
|
61
|
+
mcpServers: z.record(z.string(), McpServerSchema),
|
|
62
|
+
}).partial().passthrough();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { AgencyConfigSchema } from "./config.js";
|
|
3
|
+
describe("AgencyConfigSchema", () => {
|
|
4
|
+
it("should accept an empty config", () => {
|
|
5
|
+
const result = AgencyConfigSchema.safeParse({});
|
|
6
|
+
expect(result.success).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
it("should accept a config with existing fields", () => {
|
|
9
|
+
const result = AgencyConfigSchema.safeParse({
|
|
10
|
+
verbose: true,
|
|
11
|
+
outDir: "dist",
|
|
12
|
+
maxToolCallRounds: 5,
|
|
13
|
+
});
|
|
14
|
+
expect(result.success).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
it("should accept a config with stdio MCP server", () => {
|
|
17
|
+
const result = AgencyConfigSchema.safeParse({
|
|
18
|
+
mcpServers: {
|
|
19
|
+
filesystem: {
|
|
20
|
+
command: "npx",
|
|
21
|
+
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
|
|
22
|
+
env: { FOO: "bar" },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
expect(result.success).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
it("should accept a config with HTTP MCP server", () => {
|
|
29
|
+
const result = AgencyConfigSchema.safeParse({
|
|
30
|
+
mcpServers: {
|
|
31
|
+
weather: {
|
|
32
|
+
type: "http",
|
|
33
|
+
url: "https://weather-mcp.example.com/mcp",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
expect(result.success).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
it("should accept a config with mixed MCP servers", () => {
|
|
40
|
+
const result = AgencyConfigSchema.safeParse({
|
|
41
|
+
mcpServers: {
|
|
42
|
+
filesystem: { command: "npx", args: ["server"] },
|
|
43
|
+
weather: { type: "http", url: "https://example.com/mcp" },
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
expect(result.success).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
it("should reject an HTTP server missing url", () => {
|
|
49
|
+
const result = AgencyConfigSchema.safeParse({
|
|
50
|
+
mcpServers: {
|
|
51
|
+
bad: { type: "http" },
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
expect(result.success).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
it("should reject a stdio server missing command", () => {
|
|
57
|
+
const result = AgencyConfigSchema.safeParse({
|
|
58
|
+
mcpServers: {
|
|
59
|
+
bad: { args: ["foo"] },
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
expect(result.success).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -46,10 +46,11 @@ export class UIState {
|
|
|
46
46
|
// If the moduleId refers to a stdlib file, resolve against the actual
|
|
47
47
|
// stdlib directory (handles npm-installed packages where stdlib lives
|
|
48
48
|
// in node_modules/agency-lang/stdlib/)
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const stdlibMatch = moduleId.match(/(?:^|[/\\])stdlib[/\\](.+)$/);
|
|
50
|
+
if (stdlibMatch) {
|
|
51
|
+
const stdlibRelativePath = stdlibMatch[1];
|
|
51
52
|
for (const ext of extensions) {
|
|
52
|
-
const candidate = path.join(getStdlibDir(),
|
|
53
|
+
const candidate = path.join(getStdlibDir(), stdlibRelativePath).replace(/\.agency$/, ext);
|
|
53
54
|
if (fs.existsSync(candidate))
|
|
54
55
|
return candidate;
|
|
55
56
|
}
|
|
@@ -68,7 +68,11 @@ export declare function resolveAgencyImportPath(importPath: string, fromFile: st
|
|
|
68
68
|
* Convert an Agency import path to the path that should appear in generated
|
|
69
69
|
* TypeScript import statements.
|
|
70
70
|
*
|
|
71
|
-
* - "std::foo" ->
|
|
71
|
+
* - "std::foo" -> relative path to <stdlib-dir>/foo.js from the source file
|
|
72
72
|
* - "./foo.agency" -> "./foo.js" (relative, just extension swap)
|
|
73
|
+
*
|
|
74
|
+
* @param fromFile - Absolute path of the source file containing the import.
|
|
75
|
+
* Used to compute relative paths for stdlib imports. If not provided,
|
|
76
|
+
* falls back to absolute paths.
|
|
73
77
|
*/
|
|
74
|
-
export declare function toCompiledImportPath(importPath: string): string;
|
|
78
|
+
export declare function toCompiledImportPath(importPath: string, fromFile?: string): string;
|
package/dist/lib/importPaths.js
CHANGED
|
@@ -220,12 +220,23 @@ export function resolveAgencyImportPath(importPath, fromFile) {
|
|
|
220
220
|
* Convert an Agency import path to the path that should appear in generated
|
|
221
221
|
* TypeScript import statements.
|
|
222
222
|
*
|
|
223
|
-
* - "std::foo" ->
|
|
223
|
+
* - "std::foo" -> relative path to <stdlib-dir>/foo.js from the source file
|
|
224
224
|
* - "./foo.agency" -> "./foo.js" (relative, just extension swap)
|
|
225
|
+
*
|
|
226
|
+
* @param fromFile - Absolute path of the source file containing the import.
|
|
227
|
+
* Used to compute relative paths for stdlib imports. If not provided,
|
|
228
|
+
* falls back to absolute paths.
|
|
225
229
|
*/
|
|
226
|
-
export function toCompiledImportPath(importPath) {
|
|
230
|
+
export function toCompiledImportPath(importPath, fromFile) {
|
|
227
231
|
if (isStdlibImport(importPath)) {
|
|
228
|
-
|
|
232
|
+
const absTarget = path.join(STDLIB_DIR, normalizeStdlibPath(importPath) + ".js");
|
|
233
|
+
if (fromFile) {
|
|
234
|
+
let rel = path.relative(path.dirname(fromFile), absTarget).replace(/\\/g, "/");
|
|
235
|
+
if (!rel.startsWith("."))
|
|
236
|
+
rel = "./" + rel;
|
|
237
|
+
return rel;
|
|
238
|
+
}
|
|
239
|
+
return absTarget;
|
|
229
240
|
}
|
|
230
241
|
if (isPkgImport(importPath)) {
|
|
231
242
|
// Emit bare specifier — Node resolves it at runtime via node_modules
|
|
@@ -34,4 +34,7 @@ export declare class RunStrategy extends CompileStrategy {
|
|
|
34
34
|
constructor();
|
|
35
35
|
rewriteImport(modulePath: string, sourceFile: string): string;
|
|
36
36
|
prepareDependencies(imports: string[], sourceFile: string): void;
|
|
37
|
+
private ensureJsExists;
|
|
38
|
+
/** Extract relative .js imports from TypeScript source code. */
|
|
39
|
+
private getLocalJsImports;
|
|
37
40
|
}
|
|
@@ -37,28 +37,53 @@ export class RunStrategy extends CompileStrategy {
|
|
|
37
37
|
return modulePath.replace(/\.ts$/, ".js");
|
|
38
38
|
}
|
|
39
39
|
prepareDependencies(imports, sourceFile) {
|
|
40
|
+
const visited = new Set();
|
|
40
41
|
for (const imp of imports) {
|
|
41
42
|
if (!imp.startsWith("./") && !imp.startsWith("../"))
|
|
42
43
|
continue;
|
|
43
44
|
if (!imp.endsWith(".js"))
|
|
44
45
|
continue;
|
|
45
46
|
const resolved = path.resolve(path.dirname(sourceFile), imp);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
47
|
+
this.ensureJsExists(resolved, sourceFile, visited);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
ensureJsExists(jsPath, importer, visited) {
|
|
51
|
+
const normalized = path.normalize(jsPath);
|
|
52
|
+
if (visited.has(normalized))
|
|
53
|
+
return;
|
|
54
|
+
visited.add(normalized);
|
|
55
|
+
if (fs.existsSync(normalized))
|
|
56
|
+
return;
|
|
57
|
+
const tsPath = normalized.replace(/\.js$/, ".ts");
|
|
58
|
+
if (!fs.existsSync(tsPath)) {
|
|
59
|
+
throw new Error(`Cannot resolve import '${path.relative(path.dirname(importer), normalized)}' from '${importer}'.\n` +
|
|
60
|
+
`Tried: ${normalized}, ${tsPath} — neither file exists.`);
|
|
61
|
+
}
|
|
62
|
+
// Recursively ensure this file's dependencies exist first
|
|
63
|
+
const tsCode = fs.readFileSync(tsPath, "utf-8");
|
|
64
|
+
for (const nestedImp of this.getLocalJsImports(tsCode)) {
|
|
65
|
+
const nestedResolved = path.resolve(path.dirname(tsPath), nestedImp);
|
|
66
|
+
this.ensureJsExists(nestedResolved, tsPath, visited);
|
|
67
|
+
}
|
|
68
|
+
// Then compile this file
|
|
69
|
+
const result = transformSync(tsCode, {
|
|
70
|
+
loader: "ts",
|
|
71
|
+
format: "esm",
|
|
72
|
+
supported: { "top-level-await": true },
|
|
73
|
+
});
|
|
74
|
+
fs.writeFileSync(normalized, result.code);
|
|
75
|
+
}
|
|
76
|
+
/** Extract relative .js imports from TypeScript source code. */
|
|
77
|
+
getLocalJsImports(code) {
|
|
78
|
+
const imports = [];
|
|
79
|
+
const pattern = /\bimport\s+(?:[^'"]+?\s+from\s+)?["']([^"']+)["']/g;
|
|
80
|
+
let match;
|
|
81
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
82
|
+
const specifier = match[1];
|
|
83
|
+
if ((specifier.startsWith("./") || specifier.startsWith("../")) && specifier.endsWith(".js")) {
|
|
84
|
+
imports.push(specifier);
|
|
61
85
|
}
|
|
62
86
|
}
|
|
87
|
+
return imports;
|
|
63
88
|
}
|
|
64
89
|
}
|
|
@@ -35,3 +35,5 @@ export { DebuggerState } from "../debugger/debuggerState.js";
|
|
|
35
35
|
export { success, failure, isSuccess, isFailure, __pipeBind, __tryCall, __catchResult, } from "./result.js";
|
|
36
36
|
export type { ResultValue, ResultSuccess, ResultFailure } from "./result.js";
|
|
37
37
|
export { Schema, __validateType } from "./schema.js";
|
|
38
|
+
export { McpManager } from "./mcp/mcpManager.js";
|
|
39
|
+
export type { McpServerConfig, McpTool } from "./mcp/types.js";
|
|
@@ -24,3 +24,4 @@ export { debugStep } from "./debugger.js";
|
|
|
24
24
|
export { DebuggerState } from "../debugger/debuggerState.js";
|
|
25
25
|
export { success, failure, isSuccess, isFailure, __pipeBind, __tryCall, __catchResult, } from "./result.js";
|
|
26
26
|
export { Schema, __validateType } from "./schema.js";
|
|
27
|
+
export { McpManager } from "./mcp/mcpManager.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
const server = new McpServer({
|
|
5
|
+
name: "test-server",
|
|
6
|
+
version: "1.0.0",
|
|
7
|
+
});
|
|
8
|
+
server.tool("add", "Add two numbers together", { a: z.number(), b: z.number() }, async ({ a, b }) => {
|
|
9
|
+
return {
|
|
10
|
+
content: [{ type: "text", text: String(a + b) }],
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
const transport = new StdioServerTransport();
|
|
14
|
+
await server.connect(transport);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
+
import { McpManager } from "./mcpManager.js";
|
|
3
|
+
import { mcpToolToRegistryEntry } from "./toolAdapter.js";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const TEST_SERVER_PATH = path.join(__dirname, "__tests__", "testServer.ts");
|
|
8
|
+
describe("MCP integration", () => {
|
|
9
|
+
let manager;
|
|
10
|
+
afterEach(async () => {
|
|
11
|
+
if (manager) {
|
|
12
|
+
await manager.disconnectAll();
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
it("tools survive JSON serialization round-trip and remain callable", async () => {
|
|
16
|
+
manager = new McpManager({
|
|
17
|
+
test: {
|
|
18
|
+
command: "npx",
|
|
19
|
+
args: ["tsx", TEST_SERVER_PATH],
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
const result = await manager.getTools("test");
|
|
23
|
+
expect(result.success).toBe(true);
|
|
24
|
+
if (!result.success)
|
|
25
|
+
throw new Error("unreachable");
|
|
26
|
+
const tools = result.value;
|
|
27
|
+
const serialized = JSON.stringify(tools);
|
|
28
|
+
const deserialized = JSON.parse(serialized);
|
|
29
|
+
expect(deserialized[0].__mcpTool).toBe(true);
|
|
30
|
+
expect(deserialized[0].serverName).toBe("test");
|
|
31
|
+
expect(deserialized[0].name).toBe("test__add");
|
|
32
|
+
// After deserialization, we can still build a working handler
|
|
33
|
+
const entry = mcpToolToRegistryEntry(deserialized[0], (serverName, toolName, args) => manager.callTool(serverName, toolName, args));
|
|
34
|
+
const toolResult = await entry.handler.execute(3, 4, { ctx: null });
|
|
35
|
+
expect(toolResult).toContain("7");
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { McpServerConfig, McpTool } from "./types.js";
|
|
2
|
+
export declare class McpConnection {
|
|
3
|
+
private client;
|
|
4
|
+
private serverName;
|
|
5
|
+
private config;
|
|
6
|
+
private tools;
|
|
7
|
+
private connected;
|
|
8
|
+
constructor(serverName: string, config: McpServerConfig);
|
|
9
|
+
connect(): Promise<void>;
|
|
10
|
+
getTools(): McpTool[];
|
|
11
|
+
callTool(toolName: string, args: Record<string, unknown>): Promise<string>;
|
|
12
|
+
disconnect(): Promise<void>;
|
|
13
|
+
isConnected(): boolean;
|
|
14
|
+
}
|