agency-lang 0.0.80 → 0.0.82

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.
@@ -390,8 +390,10 @@ export class AgencyGenerator extends BaseGenerator {
390
390
  }
391
391
  processImportNameType(node) {
392
392
  switch (node.type) {
393
- case "namedImport":
394
- return `{ ${node.importedNames.join(", ")} }`;
393
+ case "namedImport": {
394
+ const names = node.importedNames.map((name) => node.safeNames?.includes(name) ? `safe ${name}` : name);
395
+ return `{ ${names.join(", ")} }`;
396
+ }
395
397
  case "namespaceImport":
396
398
  return `* as ${node.importedNames}`;
397
399
  case "defaultImport":
@@ -24,6 +24,8 @@ export declare class TypeScriptGenerator extends BaseGenerator {
24
24
  protected isInsideGraphNode: boolean;
25
25
  private parallelThreadVars;
26
26
  private loopVars;
27
+ protected safeFunctions: Record<string, boolean>;
28
+ protected importedFunctions: Record<string, boolean>;
27
29
  constructor(args?: {
28
30
  config?: AgencyConfig;
29
31
  });
@@ -104,6 +106,15 @@ export declare class TypeScriptGenerator extends BaseGenerator {
104
106
  private agencyFileToDefaultImportName;
105
107
  protected preprocess(): string;
106
108
  protected postprocess(): string;
109
+ /**
110
+ * Check if a function name refers to an impure imported function
111
+ * (imported from TS, not marked safe).
112
+ */
113
+ protected isImpureImportedFunction(functionName: string): boolean;
114
+ /**
115
+ * Check if an AST node (or its children) contains a call to an impure imported function.
116
+ */
117
+ protected containsImpureCall(node: AgencyNode): boolean;
107
118
  protected processBodyAsParts(body: AgencyNode[]): string[];
108
119
  protected processKeyword(node: Keyword): string;
109
120
  protected scopetoString(scope: ScopeType, varName?: string): string;
@@ -34,6 +34,8 @@ export class TypeScriptGenerator extends BaseGenerator {
34
34
  isInsideGraphNode = false;
35
35
  parallelThreadVars = {};
36
36
  loopVars = [];
37
+ safeFunctions = {};
38
+ importedFunctions = {};
37
39
  constructor(args = {}) {
38
40
  super(args);
39
41
  }
@@ -629,6 +631,19 @@ export class TypeScriptGenerator extends BaseGenerator {
629
631
  });
630
632
  }
631
633
  processImportStatement(node) {
634
+ // Track safe and imported functions from named imports
635
+ for (const nameType of node.importedNames) {
636
+ if (nameType.type === "namedImport") {
637
+ for (const name of nameType.importedNames) {
638
+ this.importedFunctions[name] = true;
639
+ }
640
+ if (nameType.safeNames) {
641
+ for (const safeName of nameType.safeNames) {
642
+ this.safeFunctions[safeName] = true;
643
+ }
644
+ }
645
+ }
646
+ }
632
647
  const importedNames = node.importedNames.map((name) => this.processImportNameType(name));
633
648
  return `import ${importedNames.join(", ")} from "${node.modulePath.replace(/\.agency$/, ".js")}";`;
634
649
  }
@@ -918,6 +933,30 @@ export class TypeScriptGenerator extends BaseGenerator {
918
933
  lines.push("export default graph;");
919
934
  return lines.join("\n");
920
935
  }
936
+ /**
937
+ * Check if a function name refers to an impure imported function
938
+ * (imported from TS, not marked safe).
939
+ */
940
+ isImpureImportedFunction(functionName) {
941
+ return (!!this.importedFunctions[functionName] &&
942
+ !this.safeFunctions[functionName]);
943
+ }
944
+ /**
945
+ * Check if an AST node (or its children) contains a call to an impure imported function.
946
+ */
947
+ containsImpureCall(node) {
948
+ if (node.type === "functionCall") {
949
+ if (this.isImpureImportedFunction(node.functionName)) {
950
+ return true;
951
+ }
952
+ }
953
+ if (node.type === "assignment" && node.value) {
954
+ if (this.containsImpureCall(node.value)) {
955
+ return true;
956
+ }
957
+ }
958
+ return false;
959
+ }
921
960
  /* This generates the body of a node or function separated into multiple parts.
922
961
  You can think of a part as roughly corresponding to a single statement
923
962
  (although some statements don't need their own parts, such as a newlines or type definitions).
@@ -934,6 +973,10 @@ export class TypeScriptGenerator extends BaseGenerator {
934
973
  if (!TYPES_THAT_DONT_TRIGGER_NEW_PART.includes(stmt.type)) {
935
974
  parts.push([]);
936
975
  }
976
+ // Inject __self.__retryable = false before impure imported function calls
977
+ if (this.containsImpureCall(stmt)) {
978
+ parts[parts.length - 1].push("__self.__retryable = false;\n");
979
+ }
937
980
  parts[parts.length - 1].push(this.processStatement(stmt));
938
981
  }
939
982
  const bodyCode = [];
@@ -7,7 +7,24 @@ const singleQuotedPath = seqC(char("'"), capture(many1Till(char("'")), "path"),
7
7
  const quotedPath = map(or(doubleQuotedPath, singleQuotedPath), (res) => res.path);
8
8
  export const importNodeStatmentParser = trace("importNodeStatement", seqC(set("type", "importNodeStatement"), str("import"), spaces, or(str("nodes"), str("node")), captureCaptures(parseError("expected a statement of the form `import nodes { x, y } from 'filename.agency'`", spaces, char("{"), optionalSpaces, capture(sepBy1(comma, many1WithJoin(alphanum)), "importedNodes"), optionalSpaces, char("}"), spaces, str("from"), spaces, capture(quotedPath, "agencyFile"), optionalSemicolon, optional(newline)))));
9
9
  export const importToolStatmentParser = trace("importToolStatement", seqC(set("type", "importToolStatement"), str("import"), spaces, or(str("tools"), str("tool")), captureCaptures(parseError("expected a statement of the form `import tools { x, y } from 'filename.agency'`", spaces, char("{"), optionalSpaces, capture(sepBy1(comma, many1WithJoin(alphanum)), "importedTools"), optionalSpaces, char("}"), spaces, str("from"), spaces, capture(quotedPath, "agencyFile"), optionalSemicolon, optional(newline)))));
10
- const namedImportParser = trace("namedImportParser", seqC(char("{"), optionalSpaces, capture(sepBy1(comma, many1WithJoin(alphanum)), "importedNames"), optionalSpaces, char("}"), set("type", "namedImport")));
10
+ const safeNameItem = or(map(seqC(str("safe "), capture(many1WithJoin(alphanum), "name")), (r) => ({
11
+ name: r.name,
12
+ isSafe: true,
13
+ })), map(seqC(capture(many1WithJoin(alphanum), "name")), (r) => ({
14
+ name: r.name,
15
+ isSafe: false,
16
+ })));
17
+ const namedImportParser = trace("namedImportParser", map(seqC(char("{"), optionalSpaces, capture(sepBy1(comma, safeNameItem), "items"), optionalSpaces, char("}")), (result) => {
18
+ const importedNames = [];
19
+ const safeNames = [];
20
+ for (const item of result.items) {
21
+ importedNames.push(item.name);
22
+ if (item.isSafe) {
23
+ safeNames.push(item.name);
24
+ }
25
+ }
26
+ return { type: "namedImport", importedNames, safeNames };
27
+ }));
11
28
  const namespaceImportParser = trace("namespaceImportParser", seqC(many1Till(spaces), spaces, str("as"), spaces, capture(many1WithJoin(alphanum), "importedNames"), set("type", "namespaceImport")));
12
29
  const defaultImportParser = trace("defaultImportParser", seqC(capture(many1WithJoin(alphanum), "importedNames"), set("type", "defaultImport")));
13
30
  const importNameTypeParser = sepBy(comma, or(namedImportParser, namespaceImportParser, defaultImportParser));
@@ -20,7 +20,9 @@ describe("importStatmentParser", () => {
20
20
  if (result.success) {
21
21
  expect(result.result).toEqual({
22
22
  type: "importStatement",
23
- importedNames: [{ type: "namedImport", importedNames: ["foo"] }],
23
+ importedNames: [
24
+ { type: "namedImport", importedNames: ["foo"], safeNames: [] },
25
+ ],
24
26
  modulePath: "./foo.ts",
25
27
  });
26
28
  }
@@ -34,7 +36,7 @@ describe("importStatmentParser", () => {
34
36
  type: "importStatement",
35
37
  importedNames: [
36
38
  { type: "defaultImport", importedNames: "foo" },
37
- { type: "namedImport", importedNames: ["bar"] },
39
+ { type: "namedImport", importedNames: ["bar"], safeNames: [] },
38
40
  ],
39
41
  modulePath: "./foo.ts",
40
42
  });
@@ -85,7 +87,9 @@ describe("importStatmentParser", () => {
85
87
  if (result.success) {
86
88
  expect(result.result).toEqual({
87
89
  type: "importStatement",
88
- importedNames: [{ type: "namedImport", importedNames: ["foo"] }],
90
+ importedNames: [
91
+ { type: "namedImport", importedNames: ["foo"], safeNames: [] },
92
+ ],
89
93
  modulePath: "./foo.agency",
90
94
  });
91
95
  }
@@ -105,12 +109,68 @@ describe("importStatmentParser", () => {
105
109
  expect(result.result).toEqual({
106
110
  type: "importStatement",
107
111
  importedNames: [
108
- { type: "namedImport", importedNames: ["foo", "bar", "baz"] },
112
+ {
113
+ type: "namedImport",
114
+ importedNames: ["foo", "bar", "baz"],
115
+ safeNames: [],
116
+ },
109
117
  ],
110
118
  modulePath: "myModule",
111
119
  });
112
120
  }
113
121
  });
122
+ // Safe imports
123
+ it('should parse: import { safe foo, bar } from "./tools.js"', () => {
124
+ const result = importStatmentParser('import { safe foo, bar } from "./tools.js"');
125
+ expect(result.success).toBe(true);
126
+ if (result.success) {
127
+ expect(result.result).toEqual({
128
+ type: "importStatement",
129
+ importedNames: [
130
+ {
131
+ type: "namedImport",
132
+ importedNames: ["foo", "bar"],
133
+ safeNames: ["foo"],
134
+ },
135
+ ],
136
+ modulePath: "./tools.js",
137
+ });
138
+ }
139
+ });
140
+ it('should parse: import { safe foo, safe bar, baz } from "./tools.js"', () => {
141
+ const result = importStatmentParser('import { safe foo, safe bar, baz } from "./tools.js"');
142
+ expect(result.success).toBe(true);
143
+ if (result.success) {
144
+ expect(result.result).toEqual({
145
+ type: "importStatement",
146
+ importedNames: [
147
+ {
148
+ type: "namedImport",
149
+ importedNames: ["foo", "bar", "baz"],
150
+ safeNames: ["foo", "bar"],
151
+ },
152
+ ],
153
+ modulePath: "./tools.js",
154
+ });
155
+ }
156
+ });
157
+ it('should parse: import { safe foo } from "./tools.js"', () => {
158
+ const result = importStatmentParser('import { safe foo } from "./tools.js"');
159
+ expect(result.success).toBe(true);
160
+ if (result.success) {
161
+ expect(result.result).toEqual({
162
+ type: "importStatement",
163
+ importedNames: [
164
+ {
165
+ type: "namedImport",
166
+ importedNames: ["foo"],
167
+ safeNames: ["foo"],
168
+ },
169
+ ],
170
+ modulePath: "./tools.js",
171
+ });
172
+ }
173
+ });
114
174
  // With semicolons
115
175
  it('should parse imports with semicolons', () => {
116
176
  const result = importStatmentParser('import foo from "./foo.ts";');
@@ -0,0 +1,7 @@
1
+ export declare class ToolCallError extends Error {
2
+ retryable: boolean;
3
+ originalError: unknown;
4
+ constructor(error: unknown, opts: {
5
+ retryable: boolean;
6
+ });
7
+ }
@@ -0,0 +1,12 @@
1
+ export class ToolCallError extends Error {
2
+ retryable;
3
+ originalError;
4
+ constructor(error, opts) {
5
+ super(error instanceof Error ? error.message : String(error));
6
+ this.originalError = error;
7
+ this.retryable = opts.retryable;
8
+ if (error instanceof Error && error.stack) {
9
+ this.stack = error.stack;
10
+ }
11
+ }
12
+ }
@@ -13,4 +13,5 @@ export { interrupt, isInterrupt, respondToInterrupt, approveInterrupt, rejectInt
13
13
  export { isGenerator, handleStreamingResponse } from "./streaming.js";
14
14
  export { runPrompt } from "./prompt.js";
15
15
  export type { ToolHandler } from "./prompt.js";
16
+ export { ToolCallError } from "./errors.js";
16
17
  export { setupNode, setupFunction, runNode } from "./node.js";
@@ -9,4 +9,5 @@ export { readSkillTool, readSkillToolParams, printTool, printToolParams, printJS
9
9
  export { interrupt, isInterrupt, respondToInterrupt, approveInterrupt, rejectInterrupt, modifyInterrupt, resolveInterrupt, resumeFromState, } from "./interrupts.js";
10
10
  export { isGenerator, handleStreamingResponse } from "./streaming.js";
11
11
  export { runPrompt } from "./prompt.js";
12
+ export { ToolCallError } from "./errors.js";
12
13
  export { setupNode, setupFunction, runNode } from "./node.js";
@@ -25,5 +25,6 @@ export declare function runPrompt(args: {
25
25
  stream?: boolean;
26
26
  maxToolCallRounds?: number;
27
27
  interruptData?: InterruptData;
28
+ removedTools?: string[];
28
29
  }): Promise<any>;
29
30
  export {};
@@ -5,6 +5,7 @@ import { updateTokenStats, extractResponse } from "./utils.js";
5
5
  import { callHook } from "./hooks.js";
6
6
  import { handleStreamingResponse } from "./streaming.js";
7
7
  import { ThreadStore } from "./state/threadStore.js";
8
+ import { ToolCallError } from "./errors.js";
8
9
  async function _runPrompt({ ctx, messages, tools, prompt, responseFormat, stream, clientConfig, }) {
9
10
  const startTime = performance.now();
10
11
  const startHookResult = await callHook({
@@ -92,7 +93,7 @@ async function _runPrompt({ ctx, messages, tools, prompt, responseFormat, stream
92
93
  }
93
94
  return { messages, toolCalls };
94
95
  }
95
- async function executeToolCalls({ toolCalls, toolHandlers, messages, ctx, clientConfig, interruptData, }) {
96
+ async function executeToolCalls({ toolCalls, toolHandlers, messages, ctx, clientConfig, interruptData, removedTools, toolErrorCounts, }) {
96
97
  for (const toolCall of toolCalls) {
97
98
  const handler = toolHandlers.find((h) => h.name === toolCall.name);
98
99
  if (!handler) {
@@ -141,7 +142,35 @@ async function executeToolCalls({ toolCalls, toolHandlers, messages, ctx, client
141
142
  isToolCall: true,
142
143
  });
143
144
  const toolCallStartTime = performance.now();
144
- result = await handler.execute(...params);
145
+ try {
146
+ result = await handler.execute(...params);
147
+ }
148
+ catch (error) {
149
+ const retryable = error instanceof ToolCallError ? error.retryable : false;
150
+ const errorMessage = error instanceof Error ? error.message : String(error);
151
+ toolErrorCounts[handler.name] = (toolErrorCounts[handler.name] || 0) + 1;
152
+ if (retryable && toolErrorCounts[handler.name] < 5) {
153
+ messages.push(smoltalk.toolMessage(`Error: ${errorMessage}. You may retry this tool call with corrected arguments.`, {
154
+ tool_call_id: toolCall.id,
155
+ name: toolCall.name,
156
+ }));
157
+ }
158
+ else if (retryable) {
159
+ messages.push(smoltalk.toolMessage(`Error: ${errorMessage}. This tool has failed too many times and can no longer be called.`, {
160
+ tool_call_id: toolCall.id,
161
+ name: toolCall.name,
162
+ }));
163
+ removedTools.push(handler.name);
164
+ }
165
+ else {
166
+ messages.push(smoltalk.toolMessage(`Error: ${errorMessage}. This tool failed after performing side effects and cannot be retried.`, {
167
+ tool_call_id: toolCall.id,
168
+ name: toolCall.name,
169
+ }));
170
+ removedTools.push(handler.name);
171
+ }
172
+ continue;
173
+ }
145
174
  result =
146
175
  result || `${handler.name} ran successfully but did not return a value`;
147
176
  const toolCallEndTime = performance.now();
@@ -183,7 +212,9 @@ async function executeToolCalls({ toolCalls, toolHandlers, messages, ctx, client
183
212
  return { isInterrupt: false, messages };
184
213
  }
185
214
  export async function runPrompt(args) {
186
- const { ctx, prompt, responseFormat, tools, toolHandlers = [], stream = false, maxToolCallRounds = 10, } = args;
215
+ const { ctx, prompt, responseFormat, stream = false, maxToolCallRounds = 10, removedTools = [], } = args;
216
+ let tools = (args.tools || []).filter((t) => !removedTools.includes(t.name));
217
+ let toolHandlers = (args.toolHandlers || []).filter((h) => !removedTools.includes(h.name));
187
218
  const clientConfig = ctx.getSmoltalkConfig(args.clientConfig || {});
188
219
  // console.log(color.magenta(JSON.stringify(clientConfig, null, 2)) + "\n");
189
220
  /* in order, either:
@@ -228,6 +259,7 @@ export async function runPrompt(args) {
228
259
  toolCalls = [args.interruptData.toolCall];
229
260
  }
230
261
  // Handle tool calls
262
+ const toolErrorCounts = {};
231
263
  let toolCallRound = 0;
232
264
  while (toolCalls.length > 0) {
233
265
  if (toolCallRound++ >= maxToolCallRounds) {
@@ -240,8 +272,15 @@ export async function runPrompt(args) {
240
272
  ctx,
241
273
  clientConfig,
242
274
  interruptData: args.interruptData,
275
+ removedTools,
276
+ toolErrorCounts,
243
277
  });
244
278
  messages = executeToolCallsResult.messages;
279
+ // Filter out tools that failed after side effects
280
+ if (removedTools.length > 0) {
281
+ tools = tools.filter((t) => !removedTools.includes(t.name));
282
+ toolHandlers = toolHandlers.filter((h) => !removedTools.includes(h.name));
283
+ }
245
284
  if (executeToolCallsResult.isInterrupt) {
246
285
  const { interrupt } = executeToolCallsResult;
247
286
  ctx.statelogClient.debug(`Tool call interrupted execution.`, {
@@ -1,4 +1,4 @@
1
- export declare const template = "\nexport async function {{{functionName:string}}}({{{paramList:string}}}__state: InternalFunctionState | undefined = undefined) {\n const { stack: __stack, step: __step, self: __self, threads: __threads } =\n setupFunction({ state: __state });\n\n // __state will be undefined if this function is\n // being called as a tool by an llm\n const __ctx = __state?.ctx || __globalCtx;\n const statelogClient = __ctx.statelogClient;\n const __graph = __ctx.graph;\n const __funcStartTime = performance.now();\n await callHook({ callbacks: __ctx.callbacks, name: \"onFunctionStart\", data: { functionName: \"{{{functionName}}}\", args: {{{argsObject:string}}}, isBuiltin: false } });\n\n // put all args on the state stack\n {{{paramAssignments:string}}}\n\n {{{functionBody}}}\n\n await callHook({ callbacks: __ctx.callbacks, name: \"onFunctionEnd\", data: { functionName: \"{{{functionName}}}\", timeTaken: performance.now() - __funcStartTime } });\n}\n";
1
+ export declare const template = "\nexport async function {{{functionName:string}}}({{{paramList:string}}}__state: InternalFunctionState | undefined = undefined) {\n const { stack: __stack, step: __step, self: __self, threads: __threads } =\n setupFunction({ state: __state });\n\n // __state will be undefined if this function is\n // being called as a tool by an llm\n const __ctx = __state?.ctx || __globalCtx;\n const statelogClient = __ctx.statelogClient;\n const __graph = __ctx.graph;\n const __funcStartTime = performance.now();\n await callHook({ callbacks: __ctx.callbacks, name: \"onFunctionStart\", data: { functionName: \"{{{functionName}}}\", args: {{{argsObject:string}}}, isBuiltin: false } });\n\n // put all args on the state stack\n {{{paramAssignments:string}}}\n\n __self.__retryable = __self.__retryable ?? true;\n\n try {\n {{{functionBody}}}\n } catch (__error) {\n if (__error instanceof ToolCallError) throw __error;\n throw new ToolCallError(__error, { retryable: __self.__retryable });\n }\n\n await callHook({ callbacks: __ctx.callbacks, name: \"onFunctionEnd\", data: { functionName: \"{{{functionName}}}\", timeTaken: performance.now() - __funcStartTime } });\n}\n";
2
2
  export type TemplateType = {
3
3
  functionName: string;
4
4
  paramList: string;
@@ -18,7 +18,14 @@ export async function {{{functionName:string}}}({{{paramList:string}}}__state: I
18
18
  // put all args on the state stack
19
19
  {{{paramAssignments:string}}}
20
20
 
21
+ __self.__retryable = __self.__retryable ?? true;
22
+
23
+ try {
21
24
  {{{functionBody}}}
25
+ } catch (__error) {
26
+ if (__error instanceof ToolCallError) throw __error;
27
+ throw new ToolCallError(__error, { retryable: __self.__retryable });
28
+ }
22
29
 
23
30
  await callHook({ callbacks: __ctx.callbacks, name: "onFunctionEnd", data: { functionName: "{{{functionName}}}", timeTaken: performance.now() - __funcStartTime } });
24
31
  }
@@ -1,4 +1,4 @@
1
- export declare const template = "import { fileURLToPath } from \"url\";\nimport process from \"process\";\nimport { readFileSync, writeFileSync } from \"fs\";\nimport { z } from \"zod\";\nimport { goToNode, color, nanoid, registerProvider, registerTextModel } from \"agency-lang\";\nimport * as smoltalk from \"agency-lang\";\nimport path from \"path\";\nimport type { GraphState, InternalFunctionState, Interrupt } from \"agency-lang/runtime\";\nimport {\n RuntimeContext, MessageThread, ThreadStore,\n setupNode, setupFunction, runNode, runPrompt, callHook,\n interrupt, isInterrupt,\n respondToInterrupt as _respondToInterrupt,\n approveInterrupt as _approveInterrupt,\n rejectInterrupt as _rejectInterrupt,\n resolveInterrupt as _resolveInterrupt,\n modifyInterrupt as _modifyInterrupt,\n resumeFromState as _resumeFromState,\n deepClone as __deepClone,\n not, eq, neq, lt, lte, gt, gte, and, or,\n head, tail, empty,\n builtinFetch as _builtinFetch,\n builtinFetchJSON as _builtinFetchJSON,\n builtinInput as _builtinInput,\n builtinRead as _builtinReadRaw,\n builtinWrite as _builtinWriteRaw,\n builtinReadImage as _builtinReadImageRaw,\n builtinSleep as _builtinSleep,\n builtinRound as _builtinRound,\n printJSON as _printJSON,\n print as _print,\n readSkill as _readSkillRaw,\n readSkillTool as __readSkillTool,\n readSkillToolParams as __readSkillToolParams,\n printTool as __printTool,\n printToolParams as __printToolParams,\n printJSONTool as __printJSONTool,\n printJSONToolParams as __printJSONToolParams,\n inputTool as __inputTool,\n inputToolParams as __inputToolParams,\n readTool as __readTool,\n readToolParams as __readToolParams,\n readImageTool as __readImageTool,\n readImageToolParams as __readImageToolParams,\n writeTool as __writeTool,\n writeToolParams as __writeToolParams,\n fetchTool as __fetchTool,\n fetchToolParams as __fetchToolParams,\n fetchJSONTool as __fetchJSONTool,\n fetchJSONToolParams as __fetchJSONToolParams,\n fetchJsonTool as __fetchJsonTool,\n fetchJsonToolParams as __fetchJsonToolParams,\n sleepTool as __sleepTool,\n sleepToolParams as __sleepToolParams,\n roundTool as __roundTool,\n roundToolParams as __roundToolParams,\n} from \"agency-lang/runtime\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\nconst __cwd = process.cwd();\n\nconst __globalCtx = new RuntimeContext({\n statelogConfig: {\n host: \"{{{logHost:string}}}\",\n {{#hasApiKey}}\n apiKey: \"{{{logApiKey?:string}}}\",\n {{/hasApiKey}}\n {{^hasApiKey}}\n apiKey: process.env.STATELOG_API_KEY || \"\",\n {{/hasApiKey}}\n projectId: \"{{{logProjectId:string}}}\",\n debugMode: {{{logDebugMode:boolean}}},\n },\n smoltalkDefaults: {\n {{#hasOpenAiApiKey}}\n openAiApiKey: \"{{{clientOpenAiApiKey?:string}}}\",\n {{/hasOpenAiApiKey}}\n {{^hasOpenAiApiKey}}\n openAiApiKey: process.env.OPENAI_API_KEY || \"\",\n {{/hasOpenAiApiKey}}\n {{#hasGoogleApiKey}}\n googleApiKey: \"{{{clientGoogleApiKey?:string}}}\",\n {{/hasGoogleApiKey}}\n {{^hasGoogleApiKey}}\n googleApiKey: process.env.GEMINI_API_KEY || \"\",\n {{/hasGoogleApiKey}}\n model: \"{{{clientDefaultModel:string}}}\",\n logLevel: \"{{{clientLogLevel:string}}}\",\n statelog: { \n host: \"{{{clientStatelogHost:string}}}\",\n projectId: \"{{{clientStatelogProjectId:string}}}\",\n apiKey: process.env.STATELOG_SMOLTALK_API_KEY || \"\",\n traceId: nanoid()\n }\n },\n dirname: __dirname,\n});\nconst graph = __globalCtx.graph;\n\n// Path-dependent builtin wrappers\nfunction _builtinRead(filename: string): string {\n return _builtinReadRaw({ filename, dirname: __dirname });\n}\nfunction _builtinWrite(filename: string, content: string): void {\n _builtinWriteRaw({ filename, content, dirname: __dirname });\n}\nfunction _builtinReadImage(filename: string): string {\n return _builtinReadImageRaw({ filename, dirname: __dirname });\n}\nexport function readSkill({filepath}: {filepath: string}): string {\n return _readSkillRaw({ filepath, dirname: __dirname });\n}\n\n// Interrupt re-exports bound to this module's context\nexport { interrupt, isInterrupt };\nexport const respondToInterrupt = (i: Interrupt, r: any, m?: any) => _respondToInterrupt({ ctx: __globalCtx, interrupt: i, interruptResponse: r, metadata: m });\nexport const approveInterrupt = (i: Interrupt, m?: any) => _approveInterrupt({ ctx: __globalCtx, interrupt: i, metadata: m });\nexport const rejectInterrupt = (i: Interrupt, m?: any) => _rejectInterrupt({ ctx: __globalCtx, interrupt: i, metadata: m });\nexport const modifyInterrupt = (i: Interrupt, a: any, m?: any) => _modifyInterrupt({ ctx: __globalCtx, interrupt: i, newArguments: a, metadata: m });\nexport const resolveInterrupt = (i: Interrupt, v: any, m?: any) => _resolveInterrupt({ ctx: __globalCtx, interrupt: i, value: v, metadata: m });";
1
+ export declare const template = "import { fileURLToPath } from \"url\";\nimport process from \"process\";\nimport { readFileSync, writeFileSync } from \"fs\";\nimport { z } from \"zod\";\nimport { goToNode, color, nanoid, registerProvider, registerTextModel } from \"agency-lang\";\nimport * as smoltalk from \"agency-lang\";\nimport path from \"path\";\nimport type { GraphState, InternalFunctionState, Interrupt } from \"agency-lang/runtime\";\nimport {\n RuntimeContext, MessageThread, ThreadStore,\n setupNode, setupFunction, runNode, runPrompt, callHook,\n interrupt, isInterrupt,\n respondToInterrupt as _respondToInterrupt,\n approveInterrupt as _approveInterrupt,\n rejectInterrupt as _rejectInterrupt,\n resolveInterrupt as _resolveInterrupt,\n modifyInterrupt as _modifyInterrupt,\n resumeFromState as _resumeFromState,\n ToolCallError,\n deepClone as __deepClone,\n not, eq, neq, lt, lte, gt, gte, and, or,\n head, tail, empty,\n builtinFetch as _builtinFetch,\n builtinFetchJSON as _builtinFetchJSON,\n builtinInput as _builtinInput,\n builtinRead as _builtinReadRaw,\n builtinWrite as _builtinWriteRaw,\n builtinReadImage as _builtinReadImageRaw,\n builtinSleep as _builtinSleep,\n builtinRound as _builtinRound,\n printJSON as _printJSON,\n print as _print,\n readSkill as _readSkillRaw,\n readSkillTool as __readSkillTool,\n readSkillToolParams as __readSkillToolParams,\n printTool as __printTool,\n printToolParams as __printToolParams,\n printJSONTool as __printJSONTool,\n printJSONToolParams as __printJSONToolParams,\n inputTool as __inputTool,\n inputToolParams as __inputToolParams,\n readTool as __readTool,\n readToolParams as __readToolParams,\n readImageTool as __readImageTool,\n readImageToolParams as __readImageToolParams,\n writeTool as __writeTool,\n writeToolParams as __writeToolParams,\n fetchTool as __fetchTool,\n fetchToolParams as __fetchToolParams,\n fetchJSONTool as __fetchJSONTool,\n fetchJSONToolParams as __fetchJSONToolParams,\n fetchJsonTool as __fetchJsonTool,\n fetchJsonToolParams as __fetchJsonToolParams,\n sleepTool as __sleepTool,\n sleepToolParams as __sleepToolParams,\n roundTool as __roundTool,\n roundToolParams as __roundToolParams,\n} from \"agency-lang/runtime\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\nconst __cwd = process.cwd();\n\nconst __globalCtx = new RuntimeContext({\n statelogConfig: {\n host: \"{{{logHost:string}}}\",\n {{#hasApiKey}}\n apiKey: \"{{{logApiKey?:string}}}\",\n {{/hasApiKey}}\n {{^hasApiKey}}\n apiKey: process.env.STATELOG_API_KEY || \"\",\n {{/hasApiKey}}\n projectId: \"{{{logProjectId:string}}}\",\n debugMode: {{{logDebugMode:boolean}}},\n },\n smoltalkDefaults: {\n {{#hasOpenAiApiKey}}\n openAiApiKey: \"{{{clientOpenAiApiKey?:string}}}\",\n {{/hasOpenAiApiKey}}\n {{^hasOpenAiApiKey}}\n openAiApiKey: process.env.OPENAI_API_KEY || \"\",\n {{/hasOpenAiApiKey}}\n {{#hasGoogleApiKey}}\n googleApiKey: \"{{{clientGoogleApiKey?:string}}}\",\n {{/hasGoogleApiKey}}\n {{^hasGoogleApiKey}}\n googleApiKey: process.env.GEMINI_API_KEY || \"\",\n {{/hasGoogleApiKey}}\n model: \"{{{clientDefaultModel:string}}}\",\n logLevel: \"{{{clientLogLevel:string}}}\",\n statelog: { \n host: \"{{{clientStatelogHost:string}}}\",\n projectId: \"{{{clientStatelogProjectId:string}}}\",\n apiKey: process.env.STATELOG_SMOLTALK_API_KEY || \"\",\n traceId: nanoid()\n }\n },\n dirname: __dirname,\n});\nconst graph = __globalCtx.graph;\n\n// Path-dependent builtin wrappers\nfunction _builtinRead(filename: string): string {\n return _builtinReadRaw({ filename, dirname: __dirname });\n}\nfunction _builtinWrite(filename: string, content: string): void {\n _builtinWriteRaw({ filename, content, dirname: __dirname });\n}\nfunction _builtinReadImage(filename: string): string {\n return _builtinReadImageRaw({ filename, dirname: __dirname });\n}\nexport function readSkill({filepath}: {filepath: string}): string {\n return _readSkillRaw({ filepath, dirname: __dirname });\n}\n\n// Interrupt re-exports bound to this module's context\nexport { interrupt, isInterrupt };\nexport const respondToInterrupt = (i: Interrupt, r: any, m?: any) => _respondToInterrupt({ ctx: __globalCtx, interrupt: i, interruptResponse: r, metadata: m });\nexport const approveInterrupt = (i: Interrupt, m?: any) => _approveInterrupt({ ctx: __globalCtx, interrupt: i, metadata: m });\nexport const rejectInterrupt = (i: Interrupt, m?: any) => _rejectInterrupt({ ctx: __globalCtx, interrupt: i, metadata: m });\nexport const modifyInterrupt = (i: Interrupt, a: any, m?: any) => _modifyInterrupt({ ctx: __globalCtx, interrupt: i, newArguments: a, metadata: m });\nexport const resolveInterrupt = (i: Interrupt, v: any, m?: any) => _resolveInterrupt({ ctx: __globalCtx, interrupt: i, value: v, metadata: m });";
2
2
  export type TemplateType = {
3
3
  logHost: string;
4
4
  hasApiKey: boolean;
@@ -20,6 +20,7 @@ import {
20
20
  resolveInterrupt as _resolveInterrupt,
21
21
  modifyInterrupt as _modifyInterrupt,
22
22
  resumeFromState as _resumeFromState,
23
+ ToolCallError,
23
24
  deepClone as __deepClone,
24
25
  not, eq, neq, lt, lte, gt, gte, and, or,
25
26
  head, tail, empty,
@@ -1,4 +1,4 @@
1
- export declare const template = "\nasync function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<any> {\n return runPrompt({\n ctx: __ctx,\n prompt: {{{promptCode:string}}},\n messages: __metadata?.messages || new MessageThread(),\n {{#hasResponseFormat}}\n responseFormat: z.object({\n response: {{{zodSchema:string}}}\n }),\n {{/hasResponseFormat}}\n tools: {{{tools}}},\n toolHandlers: [{{{toolHandlers:string}}}],\n clientConfig: {{{clientConfig:string}}},\n stream: {{{isStreaming:boolean}}},\n maxToolCallRounds: {{{maxToolCallRounds:number}}},\n interruptData: __state?.interruptData\n });\n}\n\n{{#isAsync}}\n__self.{{{variableName:string}}} = _{{{variableName:string}}}({{{funcCallParams:string}}});\n{{/isAsync}}\n\n{{^isAsync}}\n__self.{{{variableName:string}}} = await _{{{variableName:string}}}({{{funcCallParams:string}}});\n\n// return early from node if this is an interrupt\nif (isInterrupt(__self.{{{variableName:string}}})) {\n {{#nodeContext}}\n return { messages: __threads, data: __self.{{{variableName:string}}} };\n {{/nodeContext}}\n {{^nodeContext}}\n return __self.{{{variableName:string}}};\n {{/nodeContext}}\n}\n{{/isAsync}}";
1
+ export declare const template = "\nasync function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<any> {\n __self.__removedTools = __self.__removedTools || [];\n return runPrompt({\n ctx: __ctx,\n prompt: {{{promptCode:string}}},\n messages: __metadata?.messages || new MessageThread(),\n {{#hasResponseFormat}}\n responseFormat: z.object({\n response: {{{zodSchema:string}}}\n }),\n {{/hasResponseFormat}}\n tools: {{{tools}}},\n toolHandlers: [{{{toolHandlers:string}}}],\n clientConfig: {{{clientConfig:string}}},\n stream: {{{isStreaming:boolean}}},\n maxToolCallRounds: {{{maxToolCallRounds:number}}},\n interruptData: __state?.interruptData,\n removedTools: __self.__removedTools,\n });\n}\n\n{{#isAsync}}\n__self.{{{variableName:string}}} = _{{{variableName:string}}}({{{funcCallParams:string}}});\n{{/isAsync}}\n\n{{^isAsync}}\n__self.{{{variableName:string}}} = await _{{{variableName:string}}}({{{funcCallParams:string}}});\n\n// return early from node if this is an interrupt\nif (isInterrupt(__self.{{{variableName:string}}})) {\n {{#nodeContext}}\n return { messages: __threads, data: __self.{{{variableName:string}}} };\n {{/nodeContext}}\n {{^nodeContext}}\n return __self.{{{variableName:string}}};\n {{/nodeContext}}\n}\n{{/isAsync}}";
2
2
  export type TemplateType = {
3
3
  variableName: string;
4
4
  argsStr: string;
@@ -4,6 +4,7 @@
4
4
  import { apply } from "typestache";
5
5
  export const template = `
6
6
  async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<any> {
7
+ __self.__removedTools = __self.__removedTools || [];
7
8
  return runPrompt({
8
9
  ctx: __ctx,
9
10
  prompt: {{{promptCode:string}}},
@@ -18,7 +19,8 @@ async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<any> {
18
19
  clientConfig: {{{clientConfig:string}}},
19
20
  stream: {{{isStreaming:boolean}}},
20
21
  maxToolCallRounds: {{{maxToolCallRounds:number}}},
21
- interruptData: __state?.interruptData
22
+ interruptData: __state?.interruptData,
23
+ removedTools: __self.__removedTools,
22
24
  });
23
25
  }
24
26
 
@@ -7,6 +7,7 @@ export type ImportNameType = NamedImport | NamespaceImport | DefaultImport;
7
7
  export type NamedImport = {
8
8
  type: "namedImport";
9
9
  importedNames: string[];
10
+ safeNames: string[];
10
11
  };
11
12
  export type NamespaceImport = {
12
13
  type: "namespaceImport";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agency-lang",
3
- "version": "0.0.80",
3
+ "version": "0.0.82",
4
4
  "description": "The Agency language",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -67,7 +67,7 @@
67
67
  "nanoid": "^5.1.6",
68
68
  "ora": "^9.3.0",
69
69
  "prompts": "^2.4.2",
70
- "smoltalk": "^0.0.63",
70
+ "smoltalk": "^0.0.64",
71
71
  "tarsec": "^0.1.8",
72
72
  "termcolors": "github:egonSchiele/termcolors",
73
73
  "typestache": "^0.4.4",