@rong/agentscript 0.1.0

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 (77) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/INSTALL.md +92 -0
  3. package/LICENSE +21 -0
  4. package/README.md +246 -0
  5. package/dist/ast/constants.js +1 -0
  6. package/dist/ast/format.js +41 -0
  7. package/dist/ast/types.js +1 -0
  8. package/dist/bin/agentscript.js +234 -0
  9. package/dist/bin/input.js +19 -0
  10. package/dist/bin/repl.js +290 -0
  11. package/dist/index.js +26 -0
  12. package/dist/parser/errors.js +8 -0
  13. package/dist/parser/parser.js +661 -0
  14. package/dist/parser/tokenizer.js +246 -0
  15. package/dist/providers/llm/anthropic.js +36 -0
  16. package/dist/providers/llm/index.js +3 -0
  17. package/dist/providers/llm/ollama.js +19 -0
  18. package/dist/providers/llm/openai.js +31 -0
  19. package/dist/providers/llm/protocol.js +45 -0
  20. package/dist/providers/llm/shared.js +147 -0
  21. package/dist/providers/llm/types.js +1 -0
  22. package/dist/providers/llm/uri.js +24 -0
  23. package/dist/providers/memory/file.js +44 -0
  24. package/dist/providers/memory/host.js +66 -0
  25. package/dist/providers/memory/index.js +1 -0
  26. package/dist/providers/memory/shared.js +56 -0
  27. package/dist/providers/memory/sqlite.js +98 -0
  28. package/dist/providers/mock/index.js +32 -0
  29. package/dist/providers/tools/env.js +11 -0
  30. package/dist/providers/tools/file.js +99 -0
  31. package/dist/providers/tools/host.js +34 -0
  32. package/dist/providers/tools/http.js +40 -0
  33. package/dist/providers/tools/index.js +2 -0
  34. package/dist/providers/tools/scheme.js +16 -0
  35. package/dist/providers/tools/shared.js +92 -0
  36. package/dist/providers/tools/shell.js +80 -0
  37. package/dist/runtime/context.js +160 -0
  38. package/dist/runtime/errors.js +14 -0
  39. package/dist/runtime/evaluator.js +276 -0
  40. package/dist/runtime/generate.js +175 -0
  41. package/dist/runtime/guards.js +39 -0
  42. package/dist/runtime/input.js +38 -0
  43. package/dist/runtime/interpreter.js +314 -0
  44. package/dist/runtime/json.js +59 -0
  45. package/dist/runtime/loader.js +146 -0
  46. package/dist/runtime/scope.js +47 -0
  47. package/dist/runtime/shape.js +132 -0
  48. package/dist/runtime/trace.js +54 -0
  49. package/dist/runtime/truth.js +13 -0
  50. package/dist/runtime/types.js +1 -0
  51. package/dist/runtime/uri.js +10 -0
  52. package/dist/semantic/analyzer.js +519 -0
  53. package/dist/semantic/diagnostics.js +16 -0
  54. package/dist/utils/assert.js +3 -0
  55. package/docs/cn/context-engineering.md +389 -0
  56. package/docs/cn/language.md +478 -0
  57. package/docs/design-history/v0-design.md +365 -0
  58. package/docs/design-history/v0-implement.md +274 -0
  59. package/docs/design-history/v1-design.md +323 -0
  60. package/docs/design-history/v1-implement.md +267 -0
  61. package/docs/design-history/v2-design.md +387 -0
  62. package/docs/design-history/v2-implement.md +399 -0
  63. package/docs/en/context-engineering.md +332 -0
  64. package/docs/en/language.md +478 -0
  65. package/examples/changelog.as +29 -0
  66. package/examples/extract.as +29 -0
  67. package/examples/review.as +38 -0
  68. package/examples/summarize.as +28 -0
  69. package/examples/translate.as +33 -0
  70. package/package.json +59 -0
  71. package/tutorials/cli.as +22 -0
  72. package/tutorials/helloworld.as +14 -0
  73. package/tutorials/memory.as +19 -0
  74. package/tutorials/plan-execute.as +155 -0
  75. package/tutorials/react.as +98 -0
  76. package/tutorials/repl.as +31 -0
  77. package/tutorials/self-improve.as +60 -0
@@ -0,0 +1,160 @@
1
+ import { budgetToJson, sanitizeForJson } from "./json.js";
2
+ export function buildContext(input) {
3
+ const context = input.uses.map((item, index) => buildContextItem(item, index));
4
+ const instruction = sanitizeForJson(input.instruction);
5
+ const instructionText = renderJson(instruction);
6
+ const system = buildSystemPrompt(input.agentName, input.identity);
7
+ const returnSchema = shapeToSchema(input.returnShape);
8
+ return {
9
+ agentName: input.agentName,
10
+ model: input.model,
11
+ identity: input.identity,
12
+ system,
13
+ context,
14
+ instruction,
15
+ instructionText,
16
+ returnSchema,
17
+ budget: input.budget,
18
+ finalUserMessage: buildFinalUserMessage(context, instructionText, returnSchema),
19
+ };
20
+ }
21
+ export function shapeToSchema(shape) {
22
+ const properties = {};
23
+ const required = [];
24
+ for (const field of shape.fields) {
25
+ properties[field.name] = shapeTypeToSchema(field.type);
26
+ required.push(field.name);
27
+ }
28
+ return {
29
+ type: "object",
30
+ properties,
31
+ required,
32
+ additionalProperties: false,
33
+ };
34
+ }
35
+ const SHAPE_TYPE_TO_JSON_SCHEMA = {
36
+ string: { type: "string" },
37
+ number: { type: "number" },
38
+ boolean: { type: "boolean" },
39
+ json: {},
40
+ list: { type: "array" },
41
+ };
42
+ function shapeTypeToSchema(type) {
43
+ if (type.kind === "ListShapeType") {
44
+ return { type: "array", items: shapeTypeToSchema(type.itemType) };
45
+ }
46
+ return SHAPE_TYPE_TO_JSON_SCHEMA[type.name] ?? {};
47
+ }
48
+ function buildContextItem(item, index) {
49
+ const value = sanitizeForJson(item.value);
50
+ const clipped = clipJson(value, item.budget);
51
+ return {
52
+ index,
53
+ source: item.source,
54
+ value: clipped.value,
55
+ text: clipped.text,
56
+ budget: item.budget,
57
+ clipped: clipped.clipped,
58
+ originalSize: clipped.originalSize,
59
+ clippedSize: clipped.clippedSize,
60
+ };
61
+ }
62
+ function buildSystemPrompt(agentName, identity) {
63
+ const lines = [`You are ${agentName}.`];
64
+ for (const [key, value] of Object.entries(identity)) {
65
+ lines.push(`${key}: ${renderJson(value)}`);
66
+ }
67
+ return lines.join("\n");
68
+ }
69
+ function buildFinalUserMessage(context, instructionText, returnSchema) {
70
+ const sections = [];
71
+ if (context.length > 0) {
72
+ sections.push("Context:");
73
+ for (const item of context) {
74
+ const label = item.source ? `${item.source}:\n` : "";
75
+ sections.push(`[${item.index}] ${label}${item.text}`);
76
+ }
77
+ }
78
+ sections.push("Instruction:", instructionText);
79
+ sections.push("Return JSON matching this schema:", renderJson(returnSchema));
80
+ return sections.join("\n");
81
+ }
82
+ function renderJson(value) {
83
+ return typeof value === "string" ? value : JSON.stringify(value, null, 2);
84
+ }
85
+ function clipJson(value, budget) {
86
+ const maxChars = budgetToCharLimit(budget);
87
+ const text = renderJson(value);
88
+ if (!maxChars || text.length <= maxChars) {
89
+ return { value, text, clipped: false, originalSize: text.length, clippedSize: text.length };
90
+ }
91
+ const clippedValue = clipValueToBudget(value, maxChars);
92
+ const clippedText = renderJson(clippedValue);
93
+ return {
94
+ value: clippedValue,
95
+ text: clippedText,
96
+ clipped: clippedText !== text,
97
+ originalSize: text.length,
98
+ clippedSize: clippedText.length,
99
+ };
100
+ }
101
+ function clipValueToBudget(value, maxChars) {
102
+ if (typeof value === "string")
103
+ return value.slice(0, maxChars);
104
+ if (Array.isArray(value))
105
+ return clipArrayToBudget(value, maxChars);
106
+ if (value && typeof value === "object")
107
+ return clipObjectToBudget(value, maxChars);
108
+ return value;
109
+ }
110
+ function clipArrayToBudget(value, maxChars) {
111
+ const end = findLargestPrefix(value.length, (count) => renderJson(value.slice(0, count)).length <= maxChars);
112
+ return value.slice(0, end);
113
+ }
114
+ function clipObjectToBudget(value, maxChars) {
115
+ const entries = Object.entries(value);
116
+ const end = findLargestPrefix(entries.length, (count) => renderJson(Object.fromEntries(entries.slice(0, count))).length <= maxChars);
117
+ return Object.fromEntries(entries.slice(0, end));
118
+ }
119
+ function findLargestPrefix(length, fits) {
120
+ let low = 0;
121
+ let high = length;
122
+ while (low < high) {
123
+ const mid = Math.ceil((low + high) / 2);
124
+ if (fits(mid)) {
125
+ low = mid;
126
+ }
127
+ else {
128
+ high = mid - 1;
129
+ }
130
+ }
131
+ return low;
132
+ }
133
+ function budgetToCharLimit(budget) {
134
+ if (!budget)
135
+ return undefined;
136
+ const amount = budget.unit === "k" ? budget.amount * 1000 : budget.amount;
137
+ return Math.floor(amount);
138
+ }
139
+ export function builtContextToJson(context) {
140
+ return {
141
+ agentName: context.agentName,
142
+ model: context.model ? { name: context.model.name, uri: context.model.uri } : null,
143
+ identity: context.identity,
144
+ system: context.system,
145
+ context: context.context.map((item) => ({
146
+ index: item.index,
147
+ source: item.source ?? null,
148
+ value: item.value,
149
+ text: item.text,
150
+ budget: budgetToJson(item.budget),
151
+ clipped: item.clipped,
152
+ originalSize: item.originalSize,
153
+ clippedSize: item.clippedSize,
154
+ })),
155
+ instruction: context.instruction,
156
+ returnSchema: context.returnSchema,
157
+ budget: budgetToJson(context.budget),
158
+ finalUserMessage: context.finalUserMessage,
159
+ };
160
+ }
@@ -0,0 +1,14 @@
1
+ export class RuntimeError extends Error {
2
+ range;
3
+ constructor(message, range) {
4
+ super(formatRuntimeMessage(message, range));
5
+ this.range = range;
6
+ this.name = "RuntimeError";
7
+ }
8
+ }
9
+ function formatRuntimeMessage(message, range) {
10
+ if (!range) {
11
+ return message;
12
+ }
13
+ return `${message} at ${range.start.line}:${range.start.column}`;
14
+ }
@@ -0,0 +1,276 @@
1
+ import { assertNever } from "../utils/assert.js";
2
+ import { RuntimeError } from "./errors.js";
3
+ import { isAgentBinding, isFunctionBinding, isLlmBinding, isMemoryBinding, isObject, isToolBinding, } from "./guards.js";
4
+ import { runtimeValuesEqual, sanitizeForJson } from "./json.js";
5
+ import { uriScheme } from "./uri.js";
6
+ import { isTruthy } from "./truth.js";
7
+ export class Evaluator {
8
+ toolProvider;
9
+ memoryProvider;
10
+ trace;
11
+ generateRuntime;
12
+ host;
13
+ constructor(toolProvider, memoryProvider, trace, generateRuntime, host) {
14
+ this.toolProvider = toolProvider;
15
+ this.memoryProvider = memoryProvider;
16
+ this.trace = trace;
17
+ this.generateRuntime = generateRuntime;
18
+ this.host = host;
19
+ }
20
+ async evaluateConfig(config, scope) {
21
+ switch (config.key) {
22
+ case "model": {
23
+ const value = await this.evaluate(config.value, scope);
24
+ if (!isLlmBinding(value)) {
25
+ throw new RuntimeError("model must reference an imported llm", config.value.range);
26
+ }
27
+ return value;
28
+ }
29
+ case "role":
30
+ case "description": {
31
+ const value = await this.evaluate(config.value, scope);
32
+ if (typeof value !== "string") {
33
+ throw new RuntimeError(`${config.key} must be a string`, config.value.range);
34
+ }
35
+ return value;
36
+ }
37
+ }
38
+ }
39
+ async evaluate(expr, scope) {
40
+ switch (expr.kind) {
41
+ case "IdentifierExpr":
42
+ return scope.get(expr.name, expr.range);
43
+ case "StringExpr":
44
+ return expr.value;
45
+ case "NumberExpr":
46
+ return expr.value;
47
+ case "BooleanExpr":
48
+ return expr.value;
49
+ case "NullExpr":
50
+ return null;
51
+ case "ListExpr":
52
+ return this.evaluateAll(expr.items, scope);
53
+ case "ObjectExpr": {
54
+ const result = {};
55
+ for (const property of expr.properties) {
56
+ result[property.key] = await this.evaluate(property.value, scope);
57
+ }
58
+ return result;
59
+ }
60
+ case "ShapeObjectExpr":
61
+ throw new RuntimeError("Shape object cannot be evaluated as a runtime value", expr.range);
62
+ case "MemberExpr":
63
+ return this.evaluateMember(expr, scope);
64
+ case "IndexExpr":
65
+ return this.evaluateIndex(expr, scope);
66
+ case "UnaryExpr":
67
+ return !isTruthy(await this.evaluate(expr.value, scope));
68
+ case "BinaryExpr":
69
+ return this.evaluateBinary(expr, scope);
70
+ case "CallExpr":
71
+ return this.evaluateCall(expr, scope);
72
+ case "GenerateExpr":
73
+ return this.generateRuntime.evaluateGenerate(expr, scope);
74
+ default:
75
+ assertNever(expr);
76
+ }
77
+ }
78
+ async resolveContextUses(scope) {
79
+ const uses = [];
80
+ for (const item of scope.visibleUses()) {
81
+ uses.push({
82
+ source: item.source,
83
+ value: await this.evaluate(item.expr, item.scope),
84
+ budget: item.budget,
85
+ });
86
+ }
87
+ return uses;
88
+ }
89
+ async evaluateBinary(expr, scope) {
90
+ switch (expr.operator) {
91
+ case "and":
92
+ return isTruthy(await this.evaluate(expr.left, scope)) && isTruthy(await this.evaluate(expr.right, scope));
93
+ case "or":
94
+ return isTruthy(await this.evaluate(expr.left, scope)) || isTruthy(await this.evaluate(expr.right, scope));
95
+ case "==":
96
+ return this.valuesEqual(await this.evaluate(expr.left, scope), await this.evaluate(expr.right, scope));
97
+ case "!=":
98
+ return !this.valuesEqual(await this.evaluate(expr.left, scope), await this.evaluate(expr.right, scope));
99
+ }
100
+ }
101
+ valuesEqual(left, right) {
102
+ return runtimeValuesEqual(left, right);
103
+ }
104
+ async evaluateMember(expr, scope) {
105
+ const object = await this.evaluate(expr.object, scope);
106
+ return this.readMember(object, expr.property, expr.range);
107
+ }
108
+ async evaluateIndex(expr, scope) {
109
+ const object = await this.evaluate(expr.object, scope);
110
+ const index = await this.evaluate(expr.index, scope);
111
+ if (!Array.isArray(object)) {
112
+ throw new RuntimeError("Index access requires a list value", expr.object.range);
113
+ }
114
+ if (typeof index !== "number" || !Number.isInteger(index) || index < 0) {
115
+ throw new RuntimeError("List index must be a non-negative integer", expr.index.range);
116
+ }
117
+ if (index >= object.length) {
118
+ throw new RuntimeError(`List index ${index} is out of range`, expr.index.range);
119
+ }
120
+ return object[index];
121
+ }
122
+ readMember(object, property, range) {
123
+ if (Array.isArray(object)) {
124
+ if (property === "summary") {
125
+ return object.map((item) => sanitizeForJson(item));
126
+ }
127
+ if (property === "length") {
128
+ return object.length;
129
+ }
130
+ throw new RuntimeError(`Unknown list property '${property}'`, range);
131
+ }
132
+ if (isObject(object)) {
133
+ if (!(property in object)) {
134
+ throw new RuntimeError(`Unknown object property '${property}'`, range);
135
+ }
136
+ return object[property];
137
+ }
138
+ if (isToolBinding(object))
139
+ return { tool: object.name, method: property };
140
+ if (isMemoryBinding(object))
141
+ return { memory: object.name, method: property };
142
+ throw new RuntimeError(`Cannot read property '${property}'`, range);
143
+ }
144
+ async evaluateCall(expr, scope) {
145
+ if (expr.callee.kind === "IdentifierExpr") {
146
+ const args = await this.evaluateAll(expr.args, scope);
147
+ const callee = scope.get(expr.callee.name, expr.callee.range);
148
+ if (isFunctionBinding(callee)) {
149
+ return this.host.callFunction(this.host.requireAgent(callee.agentName), callee.name, args, expr.range);
150
+ }
151
+ if (isAgentBinding(callee)) {
152
+ const agent = this.host.requireAgent(callee.name, expr.range);
153
+ return this.host.callAgent(callee.name, this.host.resolveMainFunction(agent).name, args, expr.range);
154
+ }
155
+ throw new RuntimeError(`'${expr.callee.name}' is not callable`, expr.callee.range);
156
+ }
157
+ if (expr.callee.kind === "MemberExpr") {
158
+ return this.evaluateMemberCall(expr.callee, expr.args, scope);
159
+ }
160
+ throw new RuntimeError("Unsupported call expression", expr.range);
161
+ }
162
+ async evaluateMemberCall(callee, argsExpr, scope) {
163
+ const object = await this.evaluate(callee.object, scope);
164
+ const args = await this.evaluateAll(argsExpr, scope);
165
+ if (isAgentBinding(object)) {
166
+ return this.host.callAgent(object.name, callee.property, args, callee.range);
167
+ }
168
+ if (isToolBinding(object)) {
169
+ return this.evaluateToolCall(object, callee, args);
170
+ }
171
+ if (isMemoryBinding(object)) {
172
+ return this.evaluateMemoryCall(object, callee, args);
173
+ }
174
+ if (Array.isArray(object) && callee.property === "add") {
175
+ if (args.length !== 1) {
176
+ throw new RuntimeError("list.add expects exactly one argument", callee.range);
177
+ }
178
+ object.push(args[0]);
179
+ return object;
180
+ }
181
+ throw new RuntimeError(`Unsupported member call '${callee.property}'`, callee.range);
182
+ }
183
+ async evaluateToolCall(object, callee, args) {
184
+ const request = {
185
+ toolName: object.name,
186
+ uri: object.uri,
187
+ method: callee.property,
188
+ args,
189
+ };
190
+ let result;
191
+ try {
192
+ result = await this.toolProvider.call(request);
193
+ }
194
+ catch (error) {
195
+ const message = error instanceof Error ? error.message : String(error);
196
+ throw new RuntimeError(`Tool ${object.name}.${callee.property} (${object.uri}) failed: ${message}`, callee.range);
197
+ }
198
+ this.trace.push({
199
+ kind: "tool",
200
+ data: {
201
+ tool: object.name,
202
+ method: callee.property,
203
+ scheme: uriScheme(object.uri),
204
+ uri: object.uri,
205
+ args: sanitizeForJson(args),
206
+ result: sanitizeForJson(result),
207
+ effects: this.readEffects(result),
208
+ },
209
+ });
210
+ return result;
211
+ }
212
+ async evaluateMemoryCall(object, callee, args) {
213
+ if (args.length !== 1) {
214
+ throw new RuntimeError(`memory.${callee.property} expects exactly one argument`, callee.range);
215
+ }
216
+ let result;
217
+ try {
218
+ if (callee.property === "add") {
219
+ result = await this.memoryProvider.add({
220
+ memoryName: object.name,
221
+ uri: object.uri,
222
+ record: args[0],
223
+ });
224
+ }
225
+ else if (callee.property === "query") {
226
+ result = await this.memoryProvider.query({
227
+ memoryName: object.name,
228
+ uri: object.uri,
229
+ query: args[0],
230
+ });
231
+ }
232
+ else {
233
+ throw new RuntimeError(`Unknown memory method '${callee.property}'`, callee.range);
234
+ }
235
+ }
236
+ catch (error) {
237
+ if (error instanceof RuntimeError) {
238
+ throw error.range ? error : new RuntimeError(error.message, callee.range);
239
+ }
240
+ const message = error instanceof Error ? error.message : String(error);
241
+ throw new RuntimeError(`Memory ${object.name}.${callee.property} (${object.uri}) failed: ${message}`, callee.range);
242
+ }
243
+ const traceData = {
244
+ memory: object.name,
245
+ operation: callee.property,
246
+ uri: object.uri,
247
+ args: sanitizeForJson(args[0]),
248
+ result: sanitizeForJson(result),
249
+ count: Array.isArray(result) ? result.length : null,
250
+ };
251
+ if (callee.property === "add" && isObject(result)) {
252
+ Object.assign(traceData, {
253
+ id: typeof result.id === "string" ? result.id : null,
254
+ record: sanitizeForJson(result.record),
255
+ });
256
+ }
257
+ this.trace.push({
258
+ kind: "memory",
259
+ data: traceData,
260
+ });
261
+ return result;
262
+ }
263
+ async evaluateAll(exprs, scope) {
264
+ const values = [];
265
+ for (const expr of exprs) {
266
+ values.push(await this.evaluate(expr, scope));
267
+ }
268
+ return values;
269
+ }
270
+ readEffects(value) {
271
+ if (isObject(value) && Array.isArray(value.effects)) {
272
+ return sanitizeForJson(value.effects);
273
+ }
274
+ return null;
275
+ }
276
+ }
@@ -0,0 +1,175 @@
1
+ import { buildContext, builtContextToJson } from "./context.js";
2
+ import { RuntimeError } from "./errors.js";
3
+ import { isLlmBinding, isObject } from "./guards.js";
4
+ import { budgetToJson, sanitizeForJson } from "./json.js";
5
+ import { coerceValueToShape, validateValueAgainstShape } from "./shape.js";
6
+ export class GenerateRuntime {
7
+ llmProvider;
8
+ trace;
9
+ host;
10
+ constructor(llmProvider, trace, host) {
11
+ this.llmProvider = llmProvider;
12
+ this.trace = trace;
13
+ this.host = host;
14
+ }
15
+ async evaluateGenerate(expr, scope) {
16
+ const options = await this.parseOptions(expr, scope);
17
+ const context = await this.host.resolveContextUses(scope);
18
+ const agent = this.host.currentAgent();
19
+ const model = this.requireModel(scope, expr);
20
+ const identity = this.buildIdentity(scope, expr);
21
+ let repair;
22
+ let lastError;
23
+ for (let attempt = 1; attempt <= options.attempts; attempt += 1) {
24
+ const instruction = repair ? appendRepair(options.input, repair) : options.input;
25
+ const builtContext = buildContext({
26
+ agentName: agent.name,
27
+ model,
28
+ identity,
29
+ instruction,
30
+ returnShape: expr.returnShape,
31
+ uses: context,
32
+ budget: options.limit,
33
+ });
34
+ if (options.debug) {
35
+ writeDebugPrompt(agent.name, attempt, builtContext);
36
+ }
37
+ let rawResult;
38
+ try {
39
+ rawResult = await this.llmProvider.generate({
40
+ agentName: agent.name,
41
+ model,
42
+ identity,
43
+ instruction,
44
+ returnShape: expr.returnShape,
45
+ context,
46
+ builtContext,
47
+ budget: options.limit,
48
+ });
49
+ }
50
+ catch (error) {
51
+ if (attempt >= options.attempts || !isRepairableGenerateError(error)) {
52
+ throw withGenerateRange(error, expr.range);
53
+ }
54
+ lastError = error;
55
+ repair = {
56
+ error: errorMessage(error)
57
+ };
58
+ continue;
59
+ }
60
+ try {
61
+ const result = coerceValueToShape(rawResult, expr.returnShape);
62
+ validateValueAgainstShape(result, expr.returnShape, expr.range);
63
+ this.trace.push({
64
+ kind: "generate",
65
+ data: {
66
+ instruction: sanitizeForJson(options.input),
67
+ attempts: attempt,
68
+ budget: budgetToJson(options.limit),
69
+ debug: options.debug,
70
+ context: builtContextToJson(builtContext),
71
+ result: sanitizeForJson(result),
72
+ },
73
+ });
74
+ return result;
75
+ }
76
+ catch (error) {
77
+ if (attempt >= options.attempts) {
78
+ throw withGenerateRange(error, expr.range);
79
+ }
80
+ lastError = error;
81
+ repair = {
82
+ output: rawResult,
83
+ error: errorMessage(error),
84
+ };
85
+ }
86
+ }
87
+ throw lastError instanceof Error ? lastError : new RuntimeError("generate failed", expr.range);
88
+ }
89
+ async parseOptions(expr, scope) {
90
+ if (!expr.options.input) {
91
+ throw new RuntimeError("generate object argument requires an input field", expr.options.range);
92
+ }
93
+ const attempts = expr.options.attempts?.value ?? 1;
94
+ if (!Number.isInteger(attempts) || attempts <= 0) {
95
+ throw new RuntimeError("generate attempts must be a positive integer", expr.options.attempts?.range ?? expr.options.range);
96
+ }
97
+ return {
98
+ input: await this.host.evaluate(expr.options.input, scope),
99
+ attempts,
100
+ limit: expr.options.limit,
101
+ debug: expr.options.debug?.value ?? false,
102
+ };
103
+ }
104
+ requireModel(scope, expr) {
105
+ const model = scope.getConfig("model");
106
+ if (model === undefined || !isLlmBinding(model)) {
107
+ throw new RuntimeError("generate requires model in the current scope", expr.range);
108
+ }
109
+ return model;
110
+ }
111
+ buildIdentity(scope, expr) {
112
+ const role = scope.getConfig("role");
113
+ const description = scope.getConfig("description");
114
+ if (typeof role !== "string") {
115
+ throw new RuntimeError("generate requires role in the current scope", expr.range);
116
+ }
117
+ if (typeof description !== "string") {
118
+ throw new RuntimeError("generate requires description in the current scope", expr.range);
119
+ }
120
+ return {
121
+ role,
122
+ description,
123
+ };
124
+ }
125
+ }
126
+ function appendRepair(input, repair) {
127
+ const message = [
128
+ "Previous generation failed.",
129
+ repair.output === undefined
130
+ ? undefined
131
+ : `Previous output:\n${JSON.stringify(sanitizeForJson(repair.output), null, 2)}`,
132
+ `Error:\n${repair.error}`,
133
+ "Return corrected JSON matching the requested schema only.",
134
+ ]
135
+ .filter(Boolean)
136
+ .join("\n\n");
137
+ if (typeof input === "string") {
138
+ return `${input}\n\n${message}`;
139
+ }
140
+ if (isObject(input)) {
141
+ return {
142
+ ...input,
143
+ repair: message,
144
+ };
145
+ }
146
+ return {
147
+ input,
148
+ repair: message,
149
+ };
150
+ }
151
+ function isRepairableGenerateError(error) {
152
+ return error instanceof RuntimeError && /LLM provider did not return JSON/.test(error.message);
153
+ }
154
+ function errorMessage(error) {
155
+ return error instanceof Error ? error.message : String(error);
156
+ }
157
+ function withGenerateRange(error, range) {
158
+ if (error instanceof RuntimeError) {
159
+ return new RuntimeError(error.message, error.range ?? range);
160
+ }
161
+ return new RuntimeError(errorMessage(error), range);
162
+ }
163
+ function writeDebugPrompt(agentName, attempt, builtContext) {
164
+ const parts = [
165
+ `--- AgentScript generate debug: ${agentName} attempt ${attempt} ---`,
166
+ "System:",
167
+ builtContext.system,
168
+ "Final user message:",
169
+ builtContext.finalUserMessage,
170
+ "Return schema:",
171
+ JSON.stringify(builtContext.returnSchema, null, 2),
172
+ "--- end AgentScript generate debug ---",
173
+ ];
174
+ console.error(parts.join("\n"));
175
+ }
@@ -0,0 +1,39 @@
1
+ export function isToolBinding(value) {
2
+ return isResourceBinding(value, "tool");
3
+ }
4
+ export function isLlmBinding(value) {
5
+ return isResourceBinding(value, "llm");
6
+ }
7
+ export function isFunctionBinding(value) {
8
+ return isResourceBinding(value, "function");
9
+ }
10
+ export function isAgentBinding(value) {
11
+ return isResourceBinding(value, "agent");
12
+ }
13
+ export function isMemoryBinding(value) {
14
+ return isResourceBinding(value, "memory");
15
+ }
16
+ export function isObject(value) {
17
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !isRuntimeResource(value);
18
+ }
19
+ export function isRuntimeResource(value) {
20
+ return typeof value === "object" && value !== null && !Array.isArray(value) && "__agentScriptResource" in value;
21
+ }
22
+ const RESOURCE_KINDS_WITH_URI = new Set(["tool", "llm", "memory"]);
23
+ function isResourceBinding(value, resourceKind) {
24
+ if (typeof value !== "object" ||
25
+ value === null ||
26
+ Array.isArray(value) ||
27
+ !("__agentScriptResource" in value) ||
28
+ value.__agentScriptResource !== resourceKind) {
29
+ return false;
30
+ }
31
+ const binding = value;
32
+ if (RESOURCE_KINDS_WITH_URI.has(resourceKind)) {
33
+ return typeof binding.name === "string" && typeof binding.uri === "string";
34
+ }
35
+ if (resourceKind === "function") {
36
+ return typeof binding.name === "string" && typeof binding.agentName === "string";
37
+ }
38
+ return typeof binding.name === "string";
39
+ }