agency-lang 0.0.9 → 0.0.11

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 (30) hide show
  1. package/dist/lib/backends/agencyGenerator.d.ts +2 -0
  2. package/dist/lib/backends/agencyGenerator.js +14 -5
  3. package/dist/lib/backends/baseGenerator.d.ts +4 -1
  4. package/dist/lib/backends/baseGenerator.js +6 -1
  5. package/dist/lib/backends/graphGenerator.d.ts +1 -1
  6. package/dist/lib/backends/graphGenerator.js +14 -8
  7. package/dist/lib/backends/typescriptGenerator/builtins.js +6 -0
  8. package/dist/lib/backends/typescriptGenerator/typeToZodSchema.d.ts +1 -0
  9. package/dist/lib/backends/typescriptGenerator/typeToZodSchema.js +3 -2
  10. package/dist/lib/backends/typescriptGenerator.d.ts +2 -0
  11. package/dist/lib/backends/typescriptGenerator.js +15 -1
  12. package/dist/lib/parser.js +2 -1
  13. package/dist/lib/parsers/function.js +2 -1
  14. package/dist/lib/parsers/specialVar.d.ts +4 -0
  15. package/dist/lib/parsers/specialVar.js +8 -0
  16. package/dist/lib/templates/backends/graphGenerator/imports.d.ts +1 -1
  17. package/dist/lib/templates/backends/graphGenerator/imports.js +14 -9
  18. package/dist/lib/templates/backends/graphGenerator/runNodeFunction.d.ts +6 -0
  19. package/dist/lib/templates/backends/graphGenerator/runNodeFunction.js +13 -0
  20. package/dist/lib/templates/backends/graphGenerator/specialVar.d.ts +7 -0
  21. package/dist/lib/templates/backends/graphGenerator/specialVar.js +9 -0
  22. package/dist/lib/templates/backends/typescriptGenerator/builtinFunctions/readImage.d.ts +4 -0
  23. package/dist/lib/templates/backends/typescriptGenerator/builtinFunctions/readImage.js +17 -0
  24. package/dist/lib/templates/backends/typescriptGenerator/promptFunction.d.ts +2 -1
  25. package/dist/lib/templates/backends/typescriptGenerator/promptFunction.js +13 -2
  26. package/dist/lib/types/specialVar.d.ts +9 -0
  27. package/dist/lib/types/specialVar.js +1 -0
  28. package/dist/lib/types.d.ts +2 -1
  29. package/dist/scripts/agency.js +70 -12
  30. package/package.json +5 -6
@@ -1,3 +1,4 @@
1
+ import { SpecialVar } from "../types/specialVar.js";
1
2
  import { AgencyComment, AgencyProgram, Assignment, Literal, PromptLiteral, TypeAlias, TypeHint, VariableType } from "../types.js";
2
3
  import { AccessExpression, DotFunctionCall, DotProperty, IndexAccess } from "../types/access.js";
3
4
  import { AgencyArray, AgencyObject } from "../types/dataStructures.js";
@@ -44,6 +45,7 @@ export declare class AgencyGenerator extends BaseGenerator {
44
45
  protected processGraphNode(node: GraphNodeDefinition): string;
45
46
  protected processTool(node: FunctionDefinition): string;
46
47
  protected processUsesTool(node: UsesTool): string;
48
+ protected processSpecialVar(node: SpecialVar): string;
47
49
  private indentStr;
48
50
  }
49
51
  export declare function generateAgency(program: AgencyProgram): string;
@@ -58,7 +58,7 @@ export class AgencyGenerator extends BaseGenerator {
58
58
  // Assignment and literals
59
59
  processAssignment(node) {
60
60
  const valueCode = this.processNode(node.value).trim();
61
- return this.indentStr(`${node.variableName} = ${valueCode}\n\n`);
61
+ return this.indentStr(`${node.variableName} = ${valueCode}\n`);
62
62
  }
63
63
  generateLiteral(literal) {
64
64
  switch (literal.type) {
@@ -106,9 +106,12 @@ export class AgencyGenerator extends BaseGenerator {
106
106
  // Process body with increased indentation
107
107
  this.increaseIndent();
108
108
  this.functionScopedVariables = [...parameters];
109
+ const lines = [];
109
110
  for (const stmt of body) {
110
- result += this.processNode(stmt);
111
+ lines.push(this.processNode(stmt));
111
112
  }
113
+ const bodyCode = lines.join("").trimEnd() + "\n";
114
+ result += bodyCode;
112
115
  this.functionScopedVariables = [];
113
116
  this.decreaseIndent();
114
117
  // Close function
@@ -204,10 +207,10 @@ export class AgencyGenerator extends BaseGenerator {
204
207
  }
205
208
  // Utility methods
206
209
  processComment(node) {
207
- return this.indentStr(`// ${node.content}\n`);
210
+ return this.indentStr(`//${node.content}\n`);
208
211
  }
209
212
  processImportStatement(node) {
210
- return this.indentStr(`import {${node.importedNames}} from "${node.modulePath}"\n`);
213
+ return this.indentStr(`import ${node.importedNames}from ${node.modulePath}`);
211
214
  }
212
215
  processGraphNode(node) {
213
216
  // Graph nodes use similar syntax to functions
@@ -216,9 +219,12 @@ export class AgencyGenerator extends BaseGenerator {
216
219
  let result = this.indentStr(`node ${nodeName}(${params}) {\n`);
217
220
  this.increaseIndent();
218
221
  this.functionScopedVariables = [...parameters];
222
+ const lines = [];
219
223
  for (const stmt of body) {
220
- result += this.processNode(stmt);
224
+ lines.push(this.processNode(stmt));
221
225
  }
226
+ const bodyCode = lines.join("").trimEnd() + "\n";
227
+ result += bodyCode;
222
228
  this.functionScopedVariables = [];
223
229
  this.decreaseIndent();
224
230
  result += this.indentStr(`}\n`);
@@ -234,6 +240,9 @@ export class AgencyGenerator extends BaseGenerator {
234
240
  this.toolsUsed.push(node.toolName);
235
241
  return this.indentStr(`+${node.toolName}\n`);
236
242
  }
243
+ processSpecialVar(node) {
244
+ return this.indentStr(`@${node.name} = ${this.processNode(node.value).trim()}\n`);
245
+ }
237
246
  indentStr(str) {
238
247
  return `${this.indent()}${str}`;
239
248
  }
@@ -1,3 +1,4 @@
1
+ import { SpecialVar } from "../types/specialVar.js";
1
2
  import { AgencyComment, AgencyNode, AgencyProgram, Assignment, Literal, PromptLiteral, TypeAlias, TypeHint, TypeHintMap, VariableType } from "../types.js";
2
3
  import { AccessExpression, DotFunctionCall, DotProperty, IndexAccess } from "../types/access.js";
3
4
  import { AgencyArray, AgencyObject } from "../types/dataStructures.js";
@@ -5,6 +6,7 @@ import { FunctionCall, FunctionDefinition } from "../types/function.js";
5
6
  import { GraphNodeDefinition } from "../types/graphNode.js";
6
7
  import { ImportStatement } from "../types/importStatement.js";
7
8
  import { MatchBlock } from "../types/matchBlock.js";
9
+ import { ReturnStatement } from "../types/returnStatement.js";
8
10
  import { UsesTool } from "../types/tools.js";
9
11
  import { WhileLoop } from "../types/whileLoop.js";
10
12
  export declare class BaseGenerator {
@@ -28,6 +30,7 @@ export declare class BaseGenerator {
28
30
  protected collectFunctionSignature(node: FunctionDefinition): void;
29
31
  protected processGraphNodeName(node: GraphNodeDefinition): void;
30
32
  protected processNode(node: AgencyNode): string;
33
+ protected processSpecialVar(node: SpecialVar): string;
31
34
  protected processWhileLoop(node: WhileLoop): string;
32
35
  protected processImportStatement(node: ImportStatement): string;
33
36
  protected processTool(node: FunctionDefinition): string;
@@ -36,7 +39,7 @@ export declare class BaseGenerator {
36
39
  protected processAgencyObject(node: AgencyObject): string;
37
40
  protected processAgencyArray(node: AgencyArray): string;
38
41
  protected processComment(node: AgencyComment): string;
39
- protected processReturnStatement(node: AgencyNode): string;
42
+ protected processReturnStatement(node: ReturnStatement): string;
40
43
  protected processAccessExpression(node: AccessExpression): string;
41
44
  protected processMatchBlock(node: MatchBlock): string;
42
45
  protected processDotProperty(node: DotProperty): string;
@@ -54,7 +54,7 @@ export class BaseGenerator {
54
54
  output.push(this.generatedStatements.join(""));
55
55
  output.push(this.postprocess() + "\n");
56
56
  return {
57
- output: output.filter(Boolean).join("\n"),
57
+ output: output.filter((line) => line.trim() !== "").join("\n"),
58
58
  };
59
59
  }
60
60
  generateBuiltins() {
@@ -111,10 +111,15 @@ export class BaseGenerator {
111
111
  return "";
112
112
  case "whileLoop":
113
113
  return this.processWhileLoop(node);
114
+ case "specialVar":
115
+ return this.processSpecialVar(node);
114
116
  default:
115
117
  throw new Error(`Unhandled Agency node type: ${node.type}`);
116
118
  }
117
119
  }
120
+ processSpecialVar(node) {
121
+ return "processSpecialVar not implemented";
122
+ }
118
123
  processWhileLoop(node) {
119
124
  return "processWhileLoop not implemented";
120
125
  }
@@ -1,7 +1,7 @@
1
1
  import { AgencyProgram, FunctionCall, TypeHintMap, VariableType } from "../types.js";
2
- import { TypeScriptGenerator } from "./typescriptGenerator.js";
3
2
  import { GraphNodeDefinition } from "../types/graphNode.js";
4
3
  import { ReturnStatement } from "../types/returnStatement.js";
4
+ import { TypeScriptGenerator } from "./typescriptGenerator.js";
5
5
  export declare class GraphGenerator extends TypeScriptGenerator {
6
6
  protected typeHints: TypeHintMap;
7
7
  protected generatedStatements: string[];
@@ -1,9 +1,10 @@
1
+ import * as builtinTools from "../templates/backends/graphGenerator/builtinTools.js";
1
2
  import * as renderConditionalEdge from "../templates/backends/graphGenerator/conditionalEdge.js";
3
+ import * as goToNode from "../templates/backends/graphGenerator/goToNode.js";
4
+ import * as graphNode from "../templates/backends/graphGenerator/graphNode.js";
2
5
  import * as renderImports from "../templates/backends/graphGenerator/imports.js";
3
6
  import * as renderStartNode from "../templates/backends/graphGenerator/startNode.js";
4
- import * as graphNode from "../templates/backends/graphGenerator/graphNode.js";
5
- import * as builtinTools from "../templates/backends/graphGenerator/builtinTools.js";
6
- import * as goToNode from "../templates/backends/graphGenerator/goToNode.js";
7
+ import * as renderRunNodeFunction from "../templates/backends/graphGenerator/runNodeFunction.js";
7
8
  import { TypeScriptGenerator } from "./typescriptGenerator.js";
8
9
  import { mapFunctionName } from "./typescriptGenerator/builtins.js";
9
10
  export class GraphGenerator extends TypeScriptGenerator {
@@ -293,12 +294,17 @@ export class GraphGenerator extends TypeScriptGenerator {
293
294
  toNodes: JSON.stringify(adjacent),
294
295
  }));
295
296
  });
296
- if (!this.graphNodes.includes("main")) {
297
- throw new Error("No entrypoint found for agent: missing 'main' node. Please create a node named 'main'.");
297
+ if (this.graphNodes.includes("main")) {
298
+ lines.push(renderStartNode.default({
299
+ startNode: "main",
300
+ }));
301
+ }
302
+ for (const node of this.graphNodes) {
303
+ lines.push(renderRunNodeFunction.default({
304
+ nodeName: node,
305
+ }));
298
306
  }
299
- lines.push(renderStartNode.default({
300
- startNode: "main",
301
- }));
307
+ lines.push("export default graph;");
302
308
  return lines.join("\n");
303
309
  }
304
310
  }
@@ -1,5 +1,6 @@
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
+ import * as builtinFunctionsReadImage from "../../templates/backends/typescriptGenerator/builtinFunctions/readImage.js";
3
4
  import * as builtinFunctionsFetchJSON from "../../templates/backends/typescriptGenerator/builtinFunctions/fetchJSON.js";
4
5
  import * as builtinFunctionsFetch from "../../templates/backends/typescriptGenerator/builtinFunctions/fetch.js";
5
6
  /**
@@ -9,6 +10,7 @@ export const BUILTIN_FUNCTIONS = {
9
10
  print: "console.log",
10
11
  input: "_builtinInput",
11
12
  read: "_builtinRead",
13
+ readImage: "_builtinReadImage",
12
14
  write: "fs.writeFileSync",
13
15
  fetch: "_builtinFetch",
14
16
  fetchJSON: "_builtinFetchJSON",
@@ -42,5 +44,9 @@ export function generateBuiltinHelpers(functionsUsed) {
42
44
  const fetchFunc = builtinFunctionsFetch.default({});
43
45
  helpers.push(fetchFunc);
44
46
  }
47
+ if (functionsUsed.has("readImage")) {
48
+ const readImageFunc = builtinFunctionsReadImage.default({});
49
+ helpers.push(readImageFunc);
50
+ }
45
51
  return helpers.join("\n\n");
46
52
  }
@@ -1,4 +1,5 @@
1
1
  import { VariableType } from "../../types.js";
2
+ export declare const DEFAULT_SCHEMA = "z.string()";
2
3
  /**
3
4
  * Maps Agency types to Zod schema strings
4
5
  */
@@ -1,4 +1,5 @@
1
1
  import { escape } from "../../utils.js";
2
+ export const DEFAULT_SCHEMA = "z.string()";
2
3
  /**
3
4
  * Maps Agency types to Zod schema strings
4
5
  */
@@ -8,12 +9,12 @@ export function mapTypeToZodSchema(variableType, typeAliases) {
8
9
  case "number":
9
10
  return "z.number()";
10
11
  case "string":
11
- return "z.string()";
12
+ return DEFAULT_SCHEMA;
12
13
  case "boolean":
13
14
  return "z.boolean()";
14
15
  default:
15
16
  // Default to string for unknown types
16
- return "z.string()";
17
+ return DEFAULT_SCHEMA;
17
18
  }
18
19
  }
19
20
  else if (variableType.type === "arrayType") {
@@ -8,6 +8,7 @@ import { ReturnStatement } from "../types/returnStatement.js";
8
8
  import { UsesTool } from "../types/tools.js";
9
9
  import { ImportStatement } from "../types/importStatement.js";
10
10
  import { WhileLoop } from "../types/whileLoop.js";
11
+ import { SpecialVar } from "../types/specialVar.js";
11
12
  export declare class TypeScriptGenerator extends BaseGenerator {
12
13
  constructor();
13
14
  protected generateBuiltins(): string;
@@ -52,5 +53,6 @@ export declare class TypeScriptGenerator extends BaseGenerator {
52
53
  }): string;
53
54
  protected processImportStatement(node: ImportStatement): string;
54
55
  protected processWhileLoop(node: WhileLoop): string;
56
+ protected processSpecialVar(node: SpecialVar): string;
55
57
  }
56
58
  export declare function generateTypeScript(program: AgencyProgram): string;
@@ -3,11 +3,12 @@ import * as promptFunction from "../templates/backends/typescriptGenerator/promp
3
3
  import * as renderTool from "../templates/backends/typescriptGenerator/tool.js";
4
4
  import * as renderToolCall from "../templates/backends/typescriptGenerator/toolCall.js";
5
5
  import * as renderFunctionDefinition from "../templates/backends/typescriptGenerator/functionDefinition.js";
6
+ import * as renderSpecialVar from "../templates/backends/graphGenerator/specialVar.js";
6
7
  import { escape, zip } from "../utils.js";
7
8
  import { BaseGenerator } from "./baseGenerator.js";
8
9
  import { generateBuiltinHelpers, mapFunctionName, } from "./typescriptGenerator/builtins.js";
9
10
  import { variableTypeToString } from "./typescriptGenerator/typeToString.js";
10
- import { mapTypeToZodSchema } from "./typescriptGenerator/typeToZodSchema.js";
11
+ import { DEFAULT_SCHEMA, mapTypeToZodSchema, } from "./typescriptGenerator/typeToZodSchema.js";
11
12
  import * as builtinTools from "../templates/backends/typescriptGenerator/builtinTools.js";
12
13
  export class TypeScriptGenerator extends BaseGenerator {
13
14
  constructor() {
@@ -318,6 +319,7 @@ export class TypeScriptGenerator extends BaseGenerator {
318
319
  argsStr,
319
320
  typeString,
320
321
  promptCode,
322
+ hasResponseFormat: zodSchema !== DEFAULT_SCHEMA,
321
323
  zodSchema,
322
324
  tools,
323
325
  functionCalls,
@@ -335,6 +337,18 @@ export class TypeScriptGenerator extends BaseGenerator {
335
337
  const bodyCodeStr = bodyCodes.join("\n");
336
338
  return `while (${conditionCode}) {\n${bodyCodeStr}\n}\n`;
337
339
  }
340
+ processSpecialVar(node) {
341
+ const value = this.processNode(node.value);
342
+ switch (node.name) {
343
+ case "model":
344
+ return renderSpecialVar.default({
345
+ name: "model",
346
+ value,
347
+ });
348
+ default:
349
+ throw new Error(`Unhandled SpecialVar name: ${node.name}`);
350
+ }
351
+ }
338
352
  }
339
353
  export function generateTypeScript(program) {
340
354
  const generator = new TypeScriptGenerator();
@@ -10,8 +10,9 @@ import { matchBlockParser } from "./parsers/matchBlock.js";
10
10
  import { returnStatementParser } from "./parsers/returnStatement.js";
11
11
  import { usesToolParser } from "./parsers/tools.js";
12
12
  import { EgonLog } from "egonlog";
13
+ import { specialVarParser } from "./parsers/specialVar.js";
13
14
  export const agencyNode = (input) => {
14
- const parser = sepBy(spaces, trace("agencyParser", or(usesToolParser, importStatmentParser, graphNodeParser, typeAliasParser, whileLoopParser, typeHintParser, matchBlockParser, functionParser, returnStatementParser, accessExpressionParser, assignmentParser, functionCallParser, commentParser)));
15
+ const parser = sepBy(spaces, trace("agencyParser", or(usesToolParser, importStatmentParser, graphNodeParser, typeAliasParser, whileLoopParser, typeHintParser, matchBlockParser, functionParser, returnStatementParser, specialVarParser, accessExpressionParser, assignmentParser, functionCallParser, commentParser)));
15
16
  return parser(input);
16
17
  };
17
18
  export const agencyParser = seqC(set("type", "agencyProgram"), capture(agencyNode, "nodes"), eof);
@@ -10,10 +10,11 @@ import { typeAliasParser, typeHintParser } from "./typeHints.js";
10
10
  import { comma, optionalSpaces, varNameChar } from "./utils.js";
11
11
  import { returnStatementParser } from "./returnStatement.js";
12
12
  import { usesToolParser } from "./tools.js";
13
+ import { specialVarParser } from "./specialVar.js";
13
14
  const trim = (s) => s.trim();
14
15
  export const docStringParser = trace("docStringParser", seqC(set("type", "docString"), str('"""'), capture(map(many1Till(str('"""')), trim), "value"), str('"""')));
15
16
  export const bodyParser = trace("functionBodyParser", (input) => {
16
- const parser = sepBy(spaces, or(usesToolParser, debug(typeAliasParser, "error in typeAliasParser"), debug(typeHintParser, "error in typeHintParser"), returnStatementParser, whileLoopParser, matchBlockParser, functionParser, accessExpressionParser, assignmentParser, functionCallParser, literalParser, commentParser));
17
+ const parser = sepBy(spaces, or(usesToolParser, debug(typeAliasParser, "error in typeAliasParser"), debug(typeHintParser, "error in typeHintParser"), specialVarParser, returnStatementParser, whileLoopParser, matchBlockParser, functionParser, accessExpressionParser, assignmentParser, functionCallParser, literalParser, commentParser));
17
18
  const result = parser(input);
18
19
  return result;
19
20
  });
@@ -0,0 +1,4 @@
1
+ import { SpecialVar, SpecialVarName } from "../types/specialVar.js";
2
+ import { Parser } from "tarsec";
3
+ export declare const specialVarNameParser: Parser<SpecialVarName>;
4
+ export declare const specialVarParser: Parser<SpecialVar>;
@@ -0,0 +1,8 @@
1
+ import { specialVarNames, } from "../types/specialVar.js";
2
+ import { capture, char, or, seqC, set, str } from "tarsec";
3
+ import { optionalSpaces } from "./utils.js";
4
+ import { literalParser } from "./literals.js";
5
+ import { agencyArrayParser, agencyObjectParser } from "./dataStructures.js";
6
+ import { optionalSemicolon } from "./parserUtils.js";
7
+ export const specialVarNameParser = or(...specialVarNames.map((name) => str(name)));
8
+ export const specialVarParser = seqC(set("type", "specialVar"), char("@"), capture(specialVarNameParser, "name"), optionalSpaces, char("="), optionalSpaces, capture(or(agencyObjectParser, agencyArrayParser, literalParser), "value"), optionalSemicolon);
@@ -1,4 +1,4 @@
1
- export declare const template = "import OpenAI from \"openai\";\nimport { zodResponseFormat } from \"openai/helpers/zod\";\nimport { z } from \"zod\";\nimport * as readline from \"readline\";\nimport fs from \"fs\";\nimport { PieMachine, goToNode } from \"piemachine\";\nimport { StatelogClient } from \"statelog-client\";\nimport { nanoid } from \"nanoid\";\nimport { assistantMessage, getClient, userMessage } from \"smoltalk\";\n\nconst statelogHost = \"https://statelog.adit.io\";\nconst traceId = nanoid();\nconst statelogConfig = {\n host: statelogHost,\n traceId: traceId,\n apiKey: process.env.STATELOG_API_KEY || \"\",\n projectId: \"agency-lang\",\n debugMode: false,\n };\nconst statelogClient = new StatelogClient(statelogConfig);\nconst model = \"gpt-4o-mini\";\n\nconst client = getClient({\n openAiApiKey: process.env.OPENAI_API_KEY || \"\",\n googleApiKey: process.env.GEMINI_API_KEY || \"\",\n model,\n logLevel: \"warn\",\n});\n\ntype State = {\n messages: string[];\n data: any;\n}\n\n// enable debug logging\nconst graphConfig = {\n debug: {\n log: true,\n logData: true,\n },\n statelog: statelogConfig,\n};\n\n// Define the names of the nodes in the graph\n// Useful for type safety\nconst nodes = {{{nodes:string}}} as const;\ntype Node = (typeof nodes)[number];\n\nconst graph = new PieMachine<State, Node>(nodes, graphConfig);";
1
+ export declare const template = "import { z } from \"zod\";\nimport * as readline from \"readline\";\nimport fs from \"fs\";\nimport { PieMachine, goToNode } from \"piemachine\";\nimport { StatelogClient } from \"statelog-client\";\nimport { nanoid } from \"nanoid\";\nimport { assistantMessage, getClient, userMessage } from \"smoltalk\";\n\nconst statelogHost = \"https://statelog.adit.io\";\nconst traceId = nanoid();\nconst statelogConfig = {\n host: statelogHost,\n traceId: traceId,\n apiKey: process.env.STATELOG_API_KEY || \"\",\n projectId: \"agency-lang\",\n debugMode: false,\n };\nconst statelogClient = new StatelogClient(statelogConfig);\nconst model = \"gpt-4o-mini\";\n\n\nconst getClientWithConfig = (config = {}) => {\n const defaultConfig = {\n openAiApiKey: process.env.OPENAI_API_KEY || \"\",\n googleApiKey: process.env.GEMINI_API_KEY || \"\",\n model,\n logLevel: \"warn\",\n };\n\n return getClient({ ...defaultConfig, ...config });\n};\n\nlet client = getClientWithConfig();\n\ntype State = {\n messages: string[];\n data: any;\n}\n\n// enable debug logging\nconst graphConfig = {\n debug: {\n log: true,\n logData: true,\n },\n statelog: statelogConfig,\n};\n\n// Define the names of the nodes in the graph\n// Useful for type safety\nconst nodes = {{{nodes:string}}} as const;\ntype Node = (typeof nodes)[number];\n\nconst graph = new PieMachine<State, Node>(nodes, graphConfig);";
2
2
  export type TemplateType = {
3
3
  nodes: string;
4
4
  };
@@ -2,9 +2,7 @@
2
2
  // Source: lib/templates/backends/graphGenerator/imports.mustache
3
3
  // Any manual changes will be lost.
4
4
  import { apply } from "typestache";
5
- export const template = `import OpenAI from "openai";
6
- import { zodResponseFormat } from "openai/helpers/zod";
7
- import { z } from "zod";
5
+ export const template = `import { z } from "zod";
8
6
  import * as readline from "readline";
9
7
  import fs from "fs";
10
8
  import { PieMachine, goToNode } from "piemachine";
@@ -24,12 +22,19 @@ const statelogConfig = {
24
22
  const statelogClient = new StatelogClient(statelogConfig);
25
23
  const model = "gpt-4o-mini";
26
24
 
27
- const client = getClient({
28
- openAiApiKey: process.env.OPENAI_API_KEY || "",
29
- googleApiKey: process.env.GEMINI_API_KEY || "",
30
- model,
31
- logLevel: "warn",
32
- });
25
+
26
+ const getClientWithConfig = (config = {}) => {
27
+ const defaultConfig = {
28
+ openAiApiKey: process.env.OPENAI_API_KEY || "",
29
+ googleApiKey: process.env.GEMINI_API_KEY || "",
30
+ model,
31
+ logLevel: "warn",
32
+ };
33
+
34
+ return getClient({ ...defaultConfig, ...config });
35
+ };
36
+
37
+ let client = getClientWithConfig();
33
38
 
34
39
  type State = {
35
40
  messages: string[];
@@ -0,0 +1,6 @@
1
+ export declare const template = "export async function {{{nodeName:string}}}(data) {\n const result = await graph.run(\"{{{nodeName:string}}}\", { messages: [], data });\n return result.data;\n}\n";
2
+ export type TemplateType = {
3
+ nodeName: string;
4
+ };
5
+ declare const render: (args: TemplateType) => string;
6
+ export default render;
@@ -0,0 +1,13 @@
1
+ // THIS FILE WAS AUTO-GENERATED
2
+ // Source: lib/templates/backends/graphGenerator/runNodeFunction.mustache
3
+ // Any manual changes will be lost.
4
+ import { apply } from "typestache";
5
+ export const template = `export async function {{{nodeName:string}}}(data) {
6
+ const result = await graph.run("{{{nodeName:string}}}", { messages: [], data });
7
+ return result.data;
8
+ }
9
+ `;
10
+ const render = (args) => {
11
+ return apply(template, args);
12
+ };
13
+ export default render;
@@ -0,0 +1,7 @@
1
+ export declare const template = "client = getClientWithConfig({ {{{name}}}: {{{value}}} });";
2
+ export type TemplateType = {
3
+ name: string | boolean | number;
4
+ value: string | boolean | number;
5
+ };
6
+ declare const render: (args: TemplateType) => string;
7
+ export default render;
@@ -0,0 +1,9 @@
1
+ // THIS FILE WAS AUTO-GENERATED
2
+ // Source: lib/templates/backends/graphGenerator/specialVar.mustache
3
+ // Any manual changes will be lost.
4
+ import { apply } from "typestache";
5
+ export const template = `client = getClientWithConfig({ {{{name}}}: {{{value}}} });`;
6
+ const render = (args) => {
7
+ return apply(template, args);
8
+ };
9
+ export default render;
@@ -0,0 +1,4 @@
1
+ export declare const template = "/*\n * @param filePath The absolute or relative path to the image file.\n * @returns The Base64 string, or null if an error occurs.\n */\nfunction _builtinReadImage(filePath: string): string {\n const data = fs.readFileSync(filePath); // Synchronous file reading\n const base64String = data.toString('base64');\n return base64String;\n}";
2
+ export type TemplateType = {};
3
+ declare const render: (args: TemplateType) => string;
4
+ export default render;
@@ -0,0 +1,17 @@
1
+ // THIS FILE WAS AUTO-GENERATED
2
+ // Source: lib/templates/backends/typescriptGenerator/builtinFunctions/readImage.mustache
3
+ // Any manual changes will be lost.
4
+ import { apply } from "typestache";
5
+ export const template = `/*
6
+ * @param filePath The absolute or relative path to the image file.
7
+ * @returns The Base64 string, or null if an error occurs.
8
+ */
9
+ function _builtinReadImage(filePath: string): string {
10
+ const data = fs.readFileSync(filePath); // Synchronous file reading
11
+ const base64String = data.toString('base64');
12
+ return base64String;
13
+ }`;
14
+ const render = (args) => {
15
+ return apply(template, args);
16
+ };
17
+ export default render;
@@ -1,10 +1,11 @@
1
- export declare const template = "\nasync function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{typeString:string}}}> {\n const prompt = {{{promptCode:string}}};\n const startTime = performance.now();\n const messages: Message[] = [userMessage(prompt)];\n const tools = {{{tools}}};\n\n // Need to make sure this is always an object\n const responseFormat = z.object({\n response: {{{zodSchema:string}}}\n });\n\n let completion = await client.text({\n messages,\n tools,\n responseFormat,\n });\n\n const endTime = performance.now();\n statelogClient.promptCompletion({\n messages,\n completion,\n model,\n timeTaken: endTime - startTime,\n });\n\n if (!completion.success) {\n throw new Error(\n `Error getting response from ${model}: ${completion.error}`\n );\n }\n\n let responseMessage = completion.value;\n\n // Handle function calls\n while (responseMessage.toolCalls.length > 0) {\n // Add assistant's response with tool calls to message history\n messages.push(assistantMessage(responseMessage.output));\n let toolCallStartTime, toolCallEndTime;\n\n // Process each tool call\n for (const toolCall of responseMessage.toolCalls) {\n {{{functionCalls:string}}}\n }\n\n const nextStartTime = performance.now();\n let completion = await client.text({\n messages,\n tools,\n responseFormat,\n });\n\n const nextEndTime = performance.now();\n\n statelogClient.promptCompletion({\n messages,\n completion,\n model,\n timeTaken: nextEndTime - nextStartTime,\n });\n\n if (!completion.success) {\n throw new Error(\n `Error getting response from ${model}: ${completion.error}`\n );\n }\n responseMessage = completion.value;\n }\n\n // Add final assistant response to history\n messages.push(assistantMessage(responseMessage.output));\n\n try {\n const result = JSON.parse(responseMessage.output || \"\");\n return result.response;\n } catch (e) {\n return responseMessage.output;\n // console.error(\"Error parsing response for variable '{{{variableName:string}}}':\", e);\n // console.error(\"Full completion response:\", JSON.stringify(completion, null, 2));\n // throw e;\n }\n}\n";
1
+ export declare const template = "\nasync function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{typeString:string}}}> {\n const prompt = {{{promptCode:string}}};\n const startTime = performance.now();\n const messages: Message[] = [userMessage(prompt)];\n const tools = {{{tools}}};\n\n {{#hasResponseFormat}}\n // Need to make sure this is always an object\n const responseFormat = z.object({\n response: {{{zodSchema:string}}}\n });\n {{/hasResponseFormat}}\n {{^hasResponseFormat}}\n const responseFormat = undefined;\n {{/hasResponseFormat}}\n\n let completion = await client.text({\n messages,\n tools,\n responseFormat,\n });\n\n const endTime = performance.now();\n statelogClient.promptCompletion({\n messages,\n completion,\n model: client.getModel(),\n timeTaken: endTime - startTime,\n });\n\n if (!completion.success) {\n throw new Error(\n `Error getting response from ${model}: ${completion.error}`\n );\n }\n\n let responseMessage = completion.value;\n\n // Handle function calls\n while (responseMessage.toolCalls.length > 0) {\n // Add assistant's response with tool calls to message history\n messages.push(assistantMessage(responseMessage.output));\n let toolCallStartTime, toolCallEndTime;\n\n // Process each tool call\n for (const toolCall of responseMessage.toolCalls) {\n {{{functionCalls:string}}}\n }\n\n const nextStartTime = performance.now();\n let completion = await client.text({\n messages,\n tools,\n responseFormat,\n });\n\n const nextEndTime = performance.now();\n\n statelogClient.promptCompletion({\n messages,\n completion,\n model: client.getModel(),\n timeTaken: nextEndTime - nextStartTime,\n });\n\n if (!completion.success) {\n throw new Error(\n `Error getting response from ${model}: ${completion.error}`\n );\n }\n responseMessage = completion.value;\n }\n\n // Add final assistant response to history\n messages.push(assistantMessage(responseMessage.output));\n\n {{#hasResponseFormat}}\n try {\n const result = JSON.parse(responseMessage.output || \"\");\n return result.response;\n } catch (e) {\n return responseMessage.output;\n // console.error(\"Error parsing response for variable '{{{variableName:string}}}':\", e);\n // console.error(\"Full completion response:\", JSON.stringify(completion, null, 2));\n // throw e;\n }\n {{/hasResponseFormat}}\n\n {{^hasResponseFormat}}\n return responseMessage.output;\n {{/hasResponseFormat}}\n}\n";
2
2
  export type TemplateType = {
3
3
  variableName: string;
4
4
  argsStr: string;
5
5
  typeString: string;
6
6
  promptCode: string;
7
7
  tools: string | boolean | number;
8
+ hasResponseFormat: boolean;
8
9
  zodSchema: string;
9
10
  functionCalls: string;
10
11
  };
@@ -9,10 +9,15 @@ async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{type
9
9
  const messages: Message[] = [userMessage(prompt)];
10
10
  const tools = {{{tools}}};
11
11
 
12
+ {{#hasResponseFormat}}
12
13
  // Need to make sure this is always an object
13
14
  const responseFormat = z.object({
14
15
  response: {{{zodSchema:string}}}
15
16
  });
17
+ {{/hasResponseFormat}}
18
+ {{^hasResponseFormat}}
19
+ const responseFormat = undefined;
20
+ {{/hasResponseFormat}}
16
21
 
17
22
  let completion = await client.text({
18
23
  messages,
@@ -24,7 +29,7 @@ async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{type
24
29
  statelogClient.promptCompletion({
25
30
  messages,
26
31
  completion,
27
- model,
32
+ model: client.getModel(),
28
33
  timeTaken: endTime - startTime,
29
34
  });
30
35
 
@@ -59,7 +64,7 @@ async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{type
59
64
  statelogClient.promptCompletion({
60
65
  messages,
61
66
  completion,
62
- model,
67
+ model: client.getModel(),
63
68
  timeTaken: nextEndTime - nextStartTime,
64
69
  });
65
70
 
@@ -74,6 +79,7 @@ async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{type
74
79
  // Add final assistant response to history
75
80
  messages.push(assistantMessage(responseMessage.output));
76
81
 
82
+ {{#hasResponseFormat}}
77
83
  try {
78
84
  const result = JSON.parse(responseMessage.output || "");
79
85
  return result.response;
@@ -83,6 +89,11 @@ async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{type
83
89
  // console.error("Full completion response:", JSON.stringify(completion, null, 2));
84
90
  // throw e;
85
91
  }
92
+ {{/hasResponseFormat}}
93
+
94
+ {{^hasResponseFormat}}
95
+ return responseMessage.output;
96
+ {{/hasResponseFormat}}
86
97
  }
87
98
  `;
88
99
  const render = (args) => {
@@ -0,0 +1,9 @@
1
+ import { AgencyArray, AgencyObject } from "./dataStructures.js";
2
+ import { Literal } from "./literals.js";
3
+ export declare const specialVarNames: readonly ["model"];
4
+ export type SpecialVarName = (typeof specialVarNames)[number];
5
+ export type SpecialVar = {
6
+ type: "specialVar";
7
+ name: SpecialVarName;
8
+ value: Literal | AgencyObject | AgencyArray;
9
+ };
@@ -0,0 +1 @@
1
+ export const specialVarNames = ["model"];
@@ -9,6 +9,7 @@ import { ReturnStatement } from "./types/returnStatement.js";
9
9
  import { UsesTool } from "./types/tools.js";
10
10
  import { ImportStatement } from "./types/importStatement.js";
11
11
  import { WhileLoop } from "./types/whileLoop.js";
12
+ import { SpecialVar } from "./types/specialVar.js";
12
13
  export * from "./types/access.js";
13
14
  export * from "./types/dataStructures.js";
14
15
  export * from "./types/function.js";
@@ -29,7 +30,7 @@ export type AgencyComment = {
29
30
  type: "comment";
30
31
  content: string;
31
32
  };
32
- export type AgencyNode = TypeHint | TypeAlias | UsesTool | GraphNodeDefinition | FunctionDefinition | Assignment | Literal | FunctionCall | MatchBlock | ReturnStatement | AccessExpression | AgencyComment | AgencyObject | AgencyArray | ImportStatement | WhileLoop;
33
+ export type AgencyNode = TypeHint | TypeAlias | UsesTool | GraphNodeDefinition | FunctionDefinition | Assignment | Literal | FunctionCall | MatchBlock | ReturnStatement | AccessExpression | AgencyComment | AgencyObject | AgencyArray | ImportStatement | WhileLoop | SpecialVar;
33
34
  export type AgencyProgram = {
34
35
  type: "agencyProgram";
35
36
  nodes: AgencyNode[];
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from "child_process";
3
3
  import * as fs from "fs";
4
+ import * as path from "path";
4
5
  import { parseAgency } from "../lib/parser.js";
5
6
  import { generateGraph } from "../lib/index.js";
6
7
  import { generateAgency } from "../lib/backends/agencyGenerator.js";
@@ -10,15 +11,15 @@ Agency Language CLI
10
11
 
11
12
  Usage:
12
13
  agency help Show this help message
13
- agency compile <input> [output] Compile .agency file to TypeScript
14
+ agency compile <input> [output] Compile .agency file or directory to TypeScript
14
15
  agency run <input> [output] Compile and run .agency file
15
16
  agency format [input] Format .agency file (reads from stdin if no input)
16
17
  agency parse [input] Parse .agency file and show AST (reads from stdin if no input)
17
18
  agency <input> Compile and run .agency file (shorthand)
18
19
 
19
20
  Arguments:
20
- input Path to .agency input file (or omit to read from stdin for format/parse)
21
- output Path to output .ts file (optional)
21
+ input Path to .agency input file or directory (or omit to read from stdin for format/parse)
22
+ output Path to output .ts file (optional, ignored for directories)
22
23
  Default: <input-name>.ts
23
24
 
24
25
  Flags:
@@ -28,6 +29,7 @@ Examples:
28
29
  agency help Show help
29
30
  agency compile script.agency Compile to script.ts
30
31
  agency compile script.agency out.ts Compile to out.ts
32
+ agency compile ./scripts Compile all .agency files in directory
31
33
  agency run script.agency Compile and run script.agency
32
34
  agency -v parse script.agency Parse with verbose logging
33
35
  cat script.agency | agency format Format from stdin
@@ -70,21 +72,77 @@ function readFile(inputFile) {
70
72
  const contents = fs.readFileSync(inputFile, "utf-8");
71
73
  return contents;
72
74
  }
73
- function compile(inputFile, outputFile, verbose = false) {
74
- // Determine output file name
75
- const output = outputFile || inputFile.replace(".agency", ".ts");
75
+ function getImports(program) {
76
+ return program.nodes
77
+ .filter((node) => node.type === "importStatement")
78
+ .filter((importNode) => {
79
+ const modulePath = importNode.modulePath.trim().replace(/['"]/g, "");
80
+ return modulePath.includes(".agency");
81
+ })
82
+ .map((node) => node.modulePath.trim().replace(/['"]/g, ""));
83
+ }
84
+ const compiledFiles = new Set();
85
+ const dirSearched = new Set();
86
+ function compile(inputFile, _outputFile, verbose = false) {
87
+ // Check if the input is a directory
88
+ const stats = fs.statSync(inputFile);
89
+ if (stats.isDirectory()) {
90
+ dirSearched.add(path.resolve(inputFile));
91
+ // Find all .agency files in the directory
92
+ const files = fs.readdirSync(inputFile);
93
+ const agencyFiles = files.filter((file) => file.endsWith(".agency"));
94
+ for (const file of agencyFiles) {
95
+ const fullPath = path.join(inputFile, file);
96
+ compile(fullPath, undefined, verbose);
97
+ }
98
+ // Find all subdirectories and compile their .agency files
99
+ const subdirs = files.filter((file) => {
100
+ const fullPath = path.join(inputFile, file);
101
+ return fs.statSync(fullPath).isDirectory();
102
+ });
103
+ for (const subdir of subdirs) {
104
+ const fullSubdirPath = path.join(inputFile, subdir);
105
+ const resolvedSubdirPath = path.resolve(fullSubdirPath);
106
+ if (!dirSearched.has(resolvedSubdirPath)) {
107
+ compile(fullSubdirPath, undefined, verbose);
108
+ }
109
+ }
110
+ return null;
111
+ }
112
+ // Resolve the absolute path of the input file to avoid duplicates
113
+ const absoluteInputFile = path.resolve(inputFile);
114
+ const outputFile = _outputFile || inputFile.replace(".agency", ".ts");
115
+ // Skip if already compiled
116
+ if (compiledFiles.has(absoluteInputFile)) {
117
+ return outputFile;
118
+ }
119
+ compiledFiles.add(absoluteInputFile);
76
120
  const contents = readFile(inputFile);
77
121
  const parsedProgram = parse(contents, verbose);
78
- // Generate TypeScript code
122
+ const imports = getImports(parsedProgram);
123
+ const inputDir = path.dirname(absoluteInputFile);
124
+ for (const importPath of imports) {
125
+ const absPath = path.resolve(inputDir, importPath);
126
+ compile(absPath, undefined, verbose);
127
+ }
128
+ // Update the import path in the AST to reference the new .ts file
129
+ parsedProgram.nodes.forEach((node) => {
130
+ if (node.type === "importStatement") {
131
+ node.modulePath = node.modulePath.replace(".agency", ".ts");
132
+ }
133
+ });
79
134
  const generatedCode = generateGraph(parsedProgram);
80
- // Write to output file
81
- fs.writeFileSync(output, generatedCode, "utf-8");
82
- console.log(`Generated ${output} from ${inputFile}`);
83
- return output;
135
+ fs.writeFileSync(outputFile, generatedCode, "utf-8");
136
+ console.log(`Generated ${outputFile} from ${inputFile}`);
137
+ return outputFile;
84
138
  }
85
139
  function run(inputFile, outputFile, verbose = false) {
86
140
  // Compile the file
87
141
  const output = compile(inputFile, outputFile, verbose);
142
+ if (output === null) {
143
+ console.error("Error: No output file generated.");
144
+ process.exit(1);
145
+ }
88
146
  // Run the generated TypeScript file with Node.js
89
147
  console.log(`Running ${output}...`);
90
148
  console.log("---");
@@ -130,7 +188,7 @@ async function main() {
130
188
  break;
131
189
  case "compile":
132
190
  if (filteredArgs.length < 2) {
133
- console.error("Error: 'compile' command requires an input file");
191
+ console.error("Error: 'compile' command requires an input file or directory");
134
192
  console.error("Usage: agency compile <input> [output]");
135
193
  process.exit(1);
136
194
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agency-lang",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "description": "The Agency language",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -42,18 +42,17 @@
42
42
  "dependencies": {
43
43
  "egonlog": "^0.0.2",
44
44
  "nanoid": "^5.1.6",
45
- "openai": "^6.15.0",
46
45
  "piemachine": "^0.0.2",
47
- "smoltalk": "^0.0.8",
46
+ "smoltalk": "^0.0.11",
48
47
  "statelog-client": "^0.0.31",
49
- "tarsec": "^0.1.1",
50
- "typestache": "^0.4.4",
51
- "zod": "^4.2.1"
48
+ "zod": "^4.3.5"
52
49
  },
53
50
  "devDependencies": {
54
51
  "@types/node": "^25.0.3",
52
+ "tarsec": "^0.1.1",
55
53
  "tsc-alias": "^1.8.16",
56
54
  "typescript": "^5.9.3",
55
+ "typestache": "^0.4.4",
57
56
  "vitest": "^4.0.16"
58
57
  }
59
58
  }