agency-lang 0.0.100 → 0.0.102

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.
Files changed (87) hide show
  1. package/dist/lib/backends/typescriptBuilder.d.ts +5 -1
  2. package/dist/lib/backends/typescriptBuilder.js +38 -7
  3. package/dist/lib/backends/typescriptGenerator/builtins.js +3 -0
  4. package/dist/lib/backends/typescriptGenerator.d.ts +1 -1
  5. package/dist/lib/backends/typescriptGenerator.js +2 -2
  6. package/dist/lib/cli/auth.d.ts +4 -0
  7. package/dist/lib/cli/auth.js +60 -0
  8. package/dist/lib/cli/commands.js +23 -2
  9. package/dist/lib/cli/debug.js +8 -1
  10. package/dist/lib/config.d.ts +69 -0
  11. package/dist/lib/config.js +105 -0
  12. package/dist/lib/config.test.d.ts +1 -0
  13. package/dist/lib/config.test.js +178 -0
  14. package/dist/lib/debugger/hotReload.d.ts +11 -0
  15. package/dist/lib/debugger/hotReload.js +73 -0
  16. package/dist/lib/debugger/uiState.js +4 -3
  17. package/dist/lib/importStrategy.d.ts +3 -0
  18. package/dist/lib/importStrategy.js +40 -15
  19. package/dist/lib/runtime/hooks.d.ts +6 -0
  20. package/dist/lib/runtime/index.d.ts +2 -0
  21. package/dist/lib/runtime/index.js +1 -0
  22. package/dist/lib/runtime/mcp/__tests__/testServer.d.ts +1 -0
  23. package/dist/lib/runtime/mcp/__tests__/testServer.js +14 -0
  24. package/dist/lib/runtime/mcp/callbackServer.d.ts +20 -0
  25. package/dist/lib/runtime/mcp/callbackServer.js +86 -0
  26. package/dist/lib/runtime/mcp/callbackServer.test.d.ts +1 -0
  27. package/dist/lib/runtime/mcp/callbackServer.test.js +57 -0
  28. package/dist/lib/runtime/mcp/mcp.integration.test.d.ts +1 -0
  29. package/dist/lib/runtime/mcp/mcp.integration.test.js +37 -0
  30. package/dist/lib/runtime/mcp/mcpConnection.d.ts +23 -0
  31. package/dist/lib/runtime/mcp/mcpConnection.js +71 -0
  32. package/dist/lib/runtime/mcp/mcpConnection.test.d.ts +1 -0
  33. package/dist/lib/runtime/mcp/mcpConnection.test.js +58 -0
  34. package/dist/lib/runtime/mcp/mcpManager.d.ts +20 -0
  35. package/dist/lib/runtime/mcp/mcpManager.js +91 -0
  36. package/dist/lib/runtime/mcp/mcpManager.test.d.ts +1 -0
  37. package/dist/lib/runtime/mcp/mcpManager.test.js +128 -0
  38. package/dist/lib/runtime/mcp/oauthConnector.d.ts +41 -0
  39. package/dist/lib/runtime/mcp/oauthConnector.js +81 -0
  40. package/dist/lib/runtime/mcp/oauthConnector.test.d.ts +1 -0
  41. package/dist/lib/runtime/mcp/oauthConnector.test.js +26 -0
  42. package/dist/lib/runtime/mcp/oauthProvider.d.ts +52 -0
  43. package/dist/lib/runtime/mcp/oauthProvider.js +157 -0
  44. package/dist/lib/runtime/mcp/oauthProvider.test.d.ts +1 -0
  45. package/dist/lib/runtime/mcp/oauthProvider.test.js +151 -0
  46. package/dist/lib/runtime/mcp/tokenStore.d.ts +22 -0
  47. package/dist/lib/runtime/mcp/tokenStore.js +103 -0
  48. package/dist/lib/runtime/mcp/tokenStore.test.d.ts +1 -0
  49. package/dist/lib/runtime/mcp/tokenStore.test.js +80 -0
  50. package/dist/lib/runtime/mcp/toolAdapter.d.ts +6 -0
  51. package/dist/lib/runtime/mcp/toolAdapter.js +46 -0
  52. package/dist/lib/runtime/mcp/toolAdapter.test.d.ts +1 -0
  53. package/dist/lib/runtime/mcp/toolAdapter.test.js +68 -0
  54. package/dist/lib/runtime/mcp/types.d.ts +26 -0
  55. package/dist/lib/runtime/mcp/types.js +9 -0
  56. package/dist/lib/runtime/node.js +1 -0
  57. package/dist/lib/runtime/prompt.js +9 -1
  58. package/dist/lib/runtime/state/context.d.ts +5 -0
  59. package/dist/lib/runtime/state/context.js +14 -0
  60. package/dist/lib/templates/backends/typescriptGenerator/builtinFunctions/mcp.d.ts +4 -0
  61. package/dist/lib/templates/backends/typescriptGenerator/builtinFunctions/mcp.js +11 -0
  62. package/dist/lib/templates/backends/typescriptGenerator/imports.d.ts +1 -1
  63. package/dist/lib/templates/backends/typescriptGenerator/imports.js +1 -1
  64. package/dist/lib/types/function.d.ts +1 -1
  65. package/dist/lib/types/function.js +1 -0
  66. package/dist/lib/version.d.ts +1 -1
  67. package/dist/lib/version.js +1 -1
  68. package/dist/scripts/agency.js +22 -0
  69. package/package.json +2 -1
  70. package/stdlib/agent.js +3 -0
  71. package/stdlib/array.js +3 -0
  72. package/stdlib/clipboard.js +3 -0
  73. package/stdlib/fs.js +3 -0
  74. package/stdlib/http.js +3 -0
  75. package/stdlib/index.js +3 -0
  76. package/stdlib/lib/system.js +17 -1
  77. package/stdlib/math.js +3 -0
  78. package/stdlib/object.js +3 -0
  79. package/stdlib/path.js +3 -0
  80. package/stdlib/shell.js +3 -0
  81. package/stdlib/speech.js +3 -0
  82. package/stdlib/strategy.js +3 -0
  83. package/stdlib/system.agency +8 -1
  84. package/stdlib/system.js +112 -2
  85. package/stdlib/ui.js +3 -0
  86. package/stdlib/weather.js +3 -0
  87. package/stdlib/wikipedia.js +3 -0
@@ -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;
@@ -76,17 +76,22 @@ export class TypeScriptBuilder {
76
76
  _sourceMapBuilder = new SourceMapBuilder();
77
77
  programInfo;
78
78
  moduleId;
79
+ outputFile;
79
80
  /**
80
81
  * @param config - Agency compiler configuration (model defaults, logging, etc.)
81
82
  * @param info - Pre-collected program metadata (function definitions, graph nodes, imports, type hints)
82
83
  * @param moduleId - Unique identifier for this module (e.g., "foo.agency"), used to
83
84
  * namespace global variables in the GlobalStore so that different modules' globals
84
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.
85
89
  */
86
- constructor(config, info, moduleId) {
90
+ constructor(config, info, moduleId, outputFile) {
87
91
  this.agencyConfig = mergeDeep(this.configDefaults(), config || {});
88
92
  this.programInfo = info;
89
93
  this.moduleId = moduleId;
94
+ this.outputFile = outputFile;
90
95
  }
91
96
  configDefaults() {
92
97
  return {
@@ -1042,7 +1047,7 @@ export class TypeScriptBuilder {
1042
1047
  return ts.runnerIfElse({ id, branches, elseBranch });
1043
1048
  }
1044
1049
  processImportStatement(node) {
1045
- const from = toCompiledImportPath(node.modulePath, path.resolve(this.moduleId));
1050
+ const from = toCompiledImportPath(node.modulePath, this.outputFile ?? path.resolve(this.moduleId));
1046
1051
  const imports = node.importedNames.map((nameType) => {
1047
1052
  switch (nameType.type) {
1048
1053
  case "namedImport":
@@ -1093,7 +1098,7 @@ export class TypeScriptBuilder {
1093
1098
  return ts.importDecl({
1094
1099
  importKind: "named",
1095
1100
  names: importNames,
1096
- from: toCompiledImportPath(node.agencyFile, path.resolve(this.moduleId)),
1101
+ from: toCompiledImportPath(node.agencyFile, this.outputFile ?? path.resolve(this.moduleId)),
1097
1102
  });
1098
1103
  }
1099
1104
  // ------- TsRaw wrapper methods (template-heavy) -------
@@ -1636,6 +1641,12 @@ export class TypeScriptBuilder {
1636
1641
  })),
1637
1642
  ]), ts.statements([
1638
1643
  ts.if(ts.raw("__error instanceof RestoreSignal"), ts.statements([ts.throw("__error")])),
1644
+ ts.consoleError(ts.template([
1645
+ {
1646
+ text: "\\nAgent crashed: ",
1647
+ expr: $(ts.id("__error")).prop("message").done(),
1648
+ },
1649
+ ])),
1639
1650
  ts.return(ts.obj({
1640
1651
  messages: ts.runtime.threads,
1641
1652
  data: ts.raw(`failure(__error instanceof Error ? __error.message : String(__error), { functionName: ${JSON.stringify(nodeName)} })`),
@@ -1863,6 +1874,7 @@ export class TypeScriptBuilder {
1863
1874
  const configArg = node.arguments[1];
1864
1875
  let clientConfig;
1865
1876
  let configToolNames = [];
1877
+ let configToolExprs = [];
1866
1878
  if (configArg && configArg.type === "agencyObject") {
1867
1879
  // Extract tools from config object
1868
1880
  const toolsEntry = configArg.entries.find((e) => !("type" in e && e.type === "splat") &&
@@ -1882,6 +1894,10 @@ export class TypeScriptBuilder {
1882
1894
  configToolNames.push(toolArg.value);
1883
1895
  }
1884
1896
  }
1897
+ else if (item.type === "splat") {
1898
+ // Pass-through: spread MCP tool arrays (or any other spread) directly
1899
+ configToolExprs.push(ts.spread(this.processNode(item.value)));
1900
+ }
1885
1901
  }
1886
1902
  }
1887
1903
  // Build clientConfig without known keys
@@ -1913,12 +1929,14 @@ export class TypeScriptBuilder {
1913
1929
  const toolNodes = allToolNames.map((name) => $(ts.id("tool"))
1914
1930
  .call([ts.str(name)])
1915
1931
  .done());
1932
+ // Merge registry-resolved tools with pass-through expressions (spreads of MCP tools, etc.)
1933
+ const allToolNodes = [...toolNodes, ...configToolExprs];
1916
1934
  // Merge tools into clientConfig
1917
1935
  let mergedConfig;
1918
- if (allToolNames.length > 0) {
1936
+ if (allToolNodes.length > 0) {
1919
1937
  // Spread user config and add tools
1920
1938
  mergedConfig = ts.obj([
1921
- ts.set("tools", ts.arr(toolNodes)),
1939
+ ts.set("tools", ts.arr(allToolNodes)),
1922
1940
  ts.setSpread(clientConfig),
1923
1941
  ]);
1924
1942
  }
@@ -2419,10 +2437,23 @@ export class TypeScriptBuilder {
2419
2437
  traceConfigFields.traceFile = ts.str(this.agencyConfig.traceFile);
2420
2438
  }
2421
2439
  runtimeCtxArgs.traceConfig = ts.obj(traceConfigFields);
2422
- let runtimeCtx = ts.statements([
2440
+ const runtimeCtxStatements = [
2423
2441
  ts.constDecl("__globalCtx", ts.new(ts.id("RuntimeContext"), [ts.obj(runtimeCtxArgs)])),
2424
2442
  ts.constDecl("graph", $(ts.runtime.globalCtx).prop("graph").done()),
2425
- ]);
2443
+ ];
2444
+ if (this.agencyConfig.mcpServers) {
2445
+ // Strip secrets from the config before embedding in generated code.
2446
+ // clientId and clientSecret are resolved at runtime from env vars.
2447
+ const sanitizedServers = Object.fromEntries(Object.entries(this.agencyConfig.mcpServers).map(([name, cfg]) => {
2448
+ if ("type" in cfg && cfg.type === "http") {
2449
+ const { clientSecret, clientId, ...rest } = cfg;
2450
+ return [name, rest];
2451
+ }
2452
+ return [name, cfg];
2453
+ }));
2454
+ runtimeCtxStatements.push(ts.raw(`__globalCtx.createMcpManager(${JSON.stringify(sanitizedServers)});`));
2455
+ }
2456
+ let runtimeCtx = ts.statements(runtimeCtxStatements);
2426
2457
  return renderImports.default({
2427
2458
  runtimeContextCode: printTs(runtimeCtx),
2428
2459
  });
@@ -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
  }
@@ -0,0 +1,4 @@
1
+ import type { AgencyConfig } from "../config.js";
2
+ export declare function authServer(serverName: string, config: AgencyConfig): Promise<void>;
3
+ export declare function listAuth(): Promise<void>;
4
+ export declare function revokeAuth(serverName: string): Promise<void>;
@@ -0,0 +1,60 @@
1
+ import { TokenStore } from "../runtime/mcp/tokenStore.js";
2
+ import { OAuthConnector } from "../runtime/mcp/oauthConnector.js";
3
+ import { isOAuthServer } from "../runtime/mcp/types.js";
4
+ const store = new TokenStore();
5
+ export async function authServer(serverName, config) {
6
+ const mcpServers = config.mcpServers;
7
+ if (!mcpServers || !mcpServers[serverName]) {
8
+ console.error(`MCP server "${serverName}" not found in agency.json. Available servers: ${mcpServers ? Object.keys(mcpServers).join(", ") : "(none)"}`);
9
+ process.exit(1);
10
+ }
11
+ const serverConfig = mcpServers[serverName];
12
+ if (!isOAuthServer(serverConfig)) {
13
+ console.error(`MCP server "${serverName}" is not configured with auth: "oauth".`);
14
+ process.exit(1);
15
+ }
16
+ const httpConfig = serverConfig;
17
+ const existing = await store.loadTokens(serverName);
18
+ if (existing) {
19
+ console.log(`Token already exists for "${serverName}". Use --revoke to remove it first.`);
20
+ return;
21
+ }
22
+ console.log(`Starting OAuth authorization for "${serverName}"...`);
23
+ const connector = new OAuthConnector(serverName, httpConfig.url, store, {
24
+ timeoutMs: httpConfig.authTimeout,
25
+ clientId: httpConfig.clientId,
26
+ clientSecret: httpConfig.clientSecret,
27
+ });
28
+ try {
29
+ const { client } = await connector.connect();
30
+ console.log(`Successfully authorized "${serverName}". Token stored.`);
31
+ await client.close();
32
+ }
33
+ catch (error) {
34
+ const msg = error instanceof Error ? error.message : String(error);
35
+ console.error(`Authorization failed for "${serverName}": ${msg}`);
36
+ process.exit(1);
37
+ }
38
+ }
39
+ export async function listAuth() {
40
+ const servers = await store.listServers();
41
+ if (servers.length === 0) {
42
+ console.log("No stored OAuth tokens.");
43
+ return;
44
+ }
45
+ console.log("Stored OAuth tokens:");
46
+ for (const name of servers) {
47
+ const tokens = await store.loadTokens(name);
48
+ const hasRefresh = tokens?.refresh_token ? "yes" : "no";
49
+ console.log(` ${name} (refresh token: ${hasRefresh})`);
50
+ }
51
+ }
52
+ export async function revokeAuth(serverName) {
53
+ const tokens = await store.loadTokens(serverName);
54
+ if (!tokens) {
55
+ console.log(`No stored token for "${serverName}".`);
56
+ return;
57
+ }
58
+ await store.deleteTokens(serverName);
59
+ console.log(`Removed stored token for "${serverName}".`);
60
+ }
@@ -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
  }
@@ -79,6 +89,10 @@ export function resetCompilationCache() {
79
89
  compiledFiles.clear();
80
90
  }
81
91
  export function compile(config, inputFile, _outputFile, options) {
92
+ if (!fs.existsSync(inputFile)) {
93
+ console.error(`Error: Input file '${inputFile}' not found`);
94
+ process.exit(1);
95
+ }
82
96
  // Check if the input is a directory
83
97
  const stats = fs.statSync(inputFile);
84
98
  const verbose = config.verbose ?? false;
@@ -154,9 +168,16 @@ export function compile(config, inputFile, _outputFile, options) {
154
168
  nonAgencyImports.push(node.modulePath);
155
169
  }
156
170
  });
157
- strategy.prepareDependencies(nonAgencyImports, absoluteInputFile);
171
+ try {
172
+ strategy.prepareDependencies(nonAgencyImports, absoluteInputFile);
173
+ }
174
+ catch (error) {
175
+ console.error(error instanceof Error ? error.message : String(error));
176
+ process.exit(1);
177
+ }
158
178
  const moduleId = path.relative(process.cwd(), absoluteInputFile);
159
- const generatedCode = generateTypeScript(resolvedProgram, config, info, moduleId);
179
+ const absoluteOutputFile = path.resolve(outputFile);
180
+ const generatedCode = generateTypeScript(resolvedProgram, config, info, moduleId, absoluteOutputFile);
160
181
  if (options?.ts) {
161
182
  // TypeScript output — add @ts-nocheck so type errors don't block compilation
162
183
  fs.writeFileSync(outputFile, "// @ts-nocheck\n" + generatedCode, "utf-8");
@@ -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
- const compiledPath = resolveCompiledFile(distDir, inputFile);
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;
@@ -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,71 @@ 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
+ auth: z.ZodOptional<z.ZodLiteral<"oauth">>;
184
+ authTimeout: z.ZodOptional<z.ZodNumber>;
185
+ clientId: z.ZodOptional<z.ZodString>;
186
+ clientSecret: z.ZodOptional<z.ZodString>;
187
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
188
+ }, z.core.$strict>]>>>;
189
+ }, z.core.$loose>;
@@ -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,107 @@ 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
+ auth: z.literal("oauth").optional(),
30
+ authTimeout: z.number().optional(),
31
+ clientId: z.string().optional(),
32
+ clientSecret: z.string().optional(),
33
+ headers: z.record(z.string(), z.string()).optional(),
34
+ }).strict();
35
+ const McpServerSchema = z.union([McpStdioServerSchema, McpHttpServerSchema]);
36
+ export const AgencyConfigSchema = z.object({
37
+ verbose: z.boolean(),
38
+ outDir: z.string(),
39
+ excludeNodeTypes: z.array(z.string()),
40
+ excludeBuiltinFunctions: z.array(z.string()),
41
+ allowedFetchDomains: z.array(z.string()),
42
+ disallowedFetchDomains: z.array(z.string()),
43
+ tarsecTraceHost: z.string(),
44
+ maxToolCallRounds: z.number(),
45
+ log: z.object({ host: z.string(), projectId: z.string(), debugMode: z.boolean(), apiKey: z.string() }).partial(),
46
+ client: z.object({
47
+ logLevel: z.enum(["error", "warn", "info", "debug"]),
48
+ defaultModel: z.string(),
49
+ openAiApiKey: z.string(),
50
+ googleApiKey: z.string(),
51
+ statelog: z.object({ host: z.string(), projectId: z.string(), apiKey: z.string() }).partial(),
52
+ }).partial(),
53
+ strictTypes: z.boolean(),
54
+ typeCheck: z.boolean(),
55
+ typeCheckStrict: z.boolean(),
56
+ restrictImports: z.boolean(),
57
+ debugger: z.boolean(),
58
+ instrument: z.boolean(),
59
+ checkpoints: z.object({ maxRestores: z.number() }).partial(),
60
+ trace: z.boolean(),
61
+ traceFile: z.string(),
62
+ traceDir: z.string(),
63
+ distDir: z.string(),
64
+ test: z.object({ parallel: z.number() }).partial(),
65
+ doc: z.object({ outDir: z.string(), baseUrl: z.string() }).partial(),
66
+ mcpServers: z.record(z.string().regex(/^[A-Za-z0-9_-]+$/, "MCP server names must contain only letters, numbers, hyphens, and underscores"), McpServerSchema),
67
+ }).partial().passthrough().superRefine((data, ctx) => {
68
+ if (!data.mcpServers)
69
+ return;
70
+ for (const [name, server] of Object.entries(data.mcpServers)) {
71
+ if ("type" in server && server.type === "http") {
72
+ const httpServer = server;
73
+ if (httpServer.auth && httpServer.headers) {
74
+ ctx.addIssue({
75
+ code: z.ZodIssueCode.custom,
76
+ message: `MCP server "${name}": cannot specify both 'auth' and 'headers'`,
77
+ path: ["mcpServers", name],
78
+ });
79
+ }
80
+ if (httpServer.authTimeout && httpServer.auth !== "oauth") {
81
+ ctx.addIssue({
82
+ code: z.ZodIssueCode.custom,
83
+ message: `MCP server "${name}": 'authTimeout' requires 'auth: "oauth"'`,
84
+ path: ["mcpServers", name],
85
+ });
86
+ }
87
+ if (httpServer.clientId && httpServer.auth !== "oauth") {
88
+ ctx.addIssue({
89
+ code: z.ZodIssueCode.custom,
90
+ message: `MCP server "${name}": 'clientId' requires 'auth: "oauth"'`,
91
+ path: ["mcpServers", name],
92
+ });
93
+ }
94
+ if (httpServer.clientSecret && httpServer.auth !== "oauth") {
95
+ ctx.addIssue({
96
+ code: z.ZodIssueCode.custom,
97
+ message: `MCP server "${name}": 'clientSecret' requires 'auth: "oauth"'`,
98
+ path: ["mcpServers", name],
99
+ });
100
+ }
101
+ if (httpServer.auth === "oauth") {
102
+ try {
103
+ const parsed = new URL(httpServer.url);
104
+ const isLocalhost = ["127.0.0.1", "localhost"].includes(parsed.hostname);
105
+ if (parsed.protocol !== "https:" && !isLocalhost) {
106
+ ctx.addIssue({
107
+ code: z.ZodIssueCode.custom,
108
+ message: `MCP server "${name}": OAuth requires HTTPS (or localhost for development)`,
109
+ path: ["mcpServers", name, "url"],
110
+ });
111
+ }
112
+ }
113
+ catch {
114
+ ctx.addIssue({
115
+ code: z.ZodIssueCode.custom,
116
+ message: `MCP server "${name}": invalid URL "${httpServer.url}"`,
117
+ path: ["mcpServers", name, "url"],
118
+ });
119
+ }
120
+ }
121
+ }
122
+ }
123
+ });
@@ -0,0 +1 @@
1
+ export {};