@rong/agentscript 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  All notable changes to AgentScript will be documented in this file.
4
4
 
5
+ ## 0.1.4 - 2026-05-08
6
+
7
+ ### Added
8
+
9
+ - Added `max_output` as the explicit `generate` output budget.
10
+ - Added `temperature`, `think`, and `strict` generate options.
11
+ - Added provider request mapping for generate provider hints.
12
+
13
+ ### Changed
14
+
15
+ - Replaced generate `limit` usage with `max_output` in current docs, examples, tutorials, and fixtures.
16
+ - Expanded English and Chinese `generate` design docs with configuration semantics.
17
+ - Updated shape validation so `strict: true` disables coercion and rejects extra fields.
18
+
19
+ ## 0.1.3 - 2026-05-08
20
+
21
+ ### Added
22
+
23
+ - Added final expression return for functions.
24
+ - Added literal context labels with `use context as label`.
25
+ - Added agent role and description to prompt identity construction.
26
+ - Added trace and built context output for context labels.
27
+
28
+ ### Changed
29
+
30
+ - Updated README, examples, and language references for labeled context usage.
31
+
5
32
  ## 1.0.0 - 2026-05-07
6
33
 
7
34
  ### Added
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # AgentScript
2
2
 
3
3
  > **Agent context as code.**
4
- > `use` declares what the model can see.
4
+ > `use` declares what the model can see, with optional labels for prompt sections.
5
5
  > `generate` defines the only LLM call site and optional output shape.
6
6
  > Zero runtime dependencies. TypeScript-powered.
7
7
 
8
8
  ```agentscript
9
- use scratch.summary < 2k
9
+ use scratch.summary < 2k as observations
10
10
  generate({ input: "Answer from observations" }) -> {
11
11
  ok boolean
12
12
  text string
@@ -61,12 +61,12 @@ main agent FileSummarizer {
61
61
 
62
62
  main func(input { path string }) {
63
63
  content = File.read({ path: input.path })
64
- use input.path
65
- use content < 8k
64
+ use input.path as source path
65
+ use content < 8k as file content
66
66
 
67
67
  generate({
68
68
  input: "Summarize the file for a busy teammate"
69
- limit: 1000
69
+ max_output: 1000
70
70
  }) -> {
71
71
  title string
72
72
  summary string
@@ -113,7 +113,7 @@ It is a small language for one thing:
113
113
 
114
114
  > making LLM prompt context explicit, scoped, typed, traceable, and compilable.
115
115
 
116
- It gives you two things that general-purpose languages don't: a first-class `use` keyword that declares *which* data enters the LLM prompt, and a first-class `generate` expression that defines an LLM call with an optional output contract. Everything else — variables, functions, agents, imports, loops — exists to support this core workflow. Scopes enforce context boundaries naturally: what's `use`d in one function stays there; child scopes inherit but never leak upward. Functions can also return their final top-level expression directly, which keeps typical LLM workflows concise.
116
+ It gives you two things that general-purpose languages don't: a first-class `use` keyword that declares *which* data enters the LLM prompt and what role it plays via `as label`, and a first-class `generate` expression that defines an LLM call with an optional output contract. Everything else — variables, functions, agents, imports, loops — exists to support this core workflow. Scopes enforce context boundaries naturally: what's `use`d in one function stays there; child scopes inherit but never leak upward. Functions can also return their final top-level expression directly, which keeps typical LLM workflows concise.
117
117
 
118
118
  ## How it works
119
119
 
@@ -178,10 +178,10 @@ main agent ResearchAgent {
178
178
  main func(input {
179
179
  question string
180
180
  }) {
181
- use input.question
181
+ use input.question as user question
182
182
 
183
183
  scratch = []
184
- use scratch.summary < 2k
184
+ use scratch.summary < 2k as observations
185
185
 
186
186
  done = false
187
187
  loop until done < 6 {
@@ -195,8 +195,8 @@ main agent ResearchAgent {
195
195
  }
196
196
 
197
197
  func answer(question, scratch) {
198
- use question
199
- use scratch.summary < 2k
198
+ use question as user question
199
+ use scratch.summary < 2k as observations
200
200
  generate({ input: "Answer using only the observations" }) -> {
201
201
  ok boolean
202
202
  text string
@@ -208,7 +208,7 @@ main agent ResearchAgent {
208
208
 
209
209
  ## Key ideas
210
210
 
211
- 1. **`use` is explicit context** — nothing enters the LLM prompt unless `use`d
211
+ 1. **`use` is explicit context** — nothing enters the LLM prompt unless `use`d; `as label` names the context section
212
212
  2. **`generate` is the only LLM call site** — with a required input instruction and optional output shape
213
213
  3. **Final expression return keeps flows concise** — a function returns its final top-level expression
214
214
  4. **Scope is context boundary** — functions, agents, and blocks isolate prompt visibility
@@ -219,7 +219,7 @@ main agent ResearchAgent {
219
219
 
220
220
  | | Python / TypeScript | AgentScript |
221
221
  |---|---|---|
222
- | Context management | Implicit (string concatenation, array append) | Explicit (`use` declaration) |
222
+ | Context management | Implicit (string concatenation, array append) | Explicit (`use` declaration, optional `as label`) |
223
223
  | LLM call site | Anywhere in the code | One `generate` expression |
224
224
  | Context isolation | Manual discipline | Scope-inherited, auto-isolated |
225
225
  | Trace / audit | External tooling needed | Built-in, per-call |
@@ -254,8 +254,8 @@ agentscript examples/review.as --quiet # value only, no trace
254
254
 
255
255
  | Language | Links |
256
256
  |----------|-------|
257
- | English | [Language Reference](docs/en/language.md) · [Context Engineering](docs/en/context-engineering.md) · [Design History](docs/design-history/) |
258
- | 中文 | [README-CN](./README-CN.md) · [语言参考](docs/cn/language.md) · [Context Engineering](docs/cn/context-engineering.md) |
257
+ | English | [Language Reference](docs/en/language.md) · [Context Engineering](docs/en/context-engineering.md) · [`use ... as ...`](docs/en/use-as.md) · [`generate`](docs/en/generate.md) · [Design History](docs/design-history/) |
258
+ | 中文 | [README-CN](./README-CN.md) · [语言参考](docs/cn/language.md) · [Context Engineering](docs/cn/context-engineering.md) · [`use ... as ...`](docs/cn/use-as.md) · [`generate`](docs/cn/generate.md) |
259
259
 
260
260
  ### Design principles
261
261
 
@@ -176,13 +176,26 @@ class Parser {
176
176
  const start = this.consume("use").range.start;
177
177
  const value = this.parseLogicalOr();
178
178
  const budget = this.match("<") ? this.parseBudget() : undefined;
179
+ const label = this.match("as") ? this.parseUseLabel() : undefined;
179
180
  return {
180
181
  kind: "UseStmt",
181
182
  value,
182
183
  budget,
184
+ label,
183
185
  range: { start, end: this.previous().range.end }
184
186
  };
185
187
  }
188
+ parseUseLabel() {
189
+ const tokens = [];
190
+ const line = this.previous().range.start.line;
191
+ while (!this.isAtEnd() && !this.check("}") && this.peek().range.start.line === line) {
192
+ tokens.push(this.advance());
193
+ }
194
+ if (tokens.length === 0) {
195
+ throw this.error("Expected context label after 'as'");
196
+ }
197
+ return stringifyLabelTokens(tokens);
198
+ }
186
199
  parseConfigValue(key) {
187
200
  if (key === "model") {
188
201
  return this.parsePostfix();
@@ -421,16 +434,19 @@ class Parser {
421
434
  const properties = [];
422
435
  let input;
423
436
  let attempts;
424
- let limit;
437
+ let maxOutput;
438
+ let temperature;
439
+ let think;
440
+ let strict;
425
441
  let debug;
426
442
  while (!this.check("}") && !this.isAtEnd()) {
427
443
  const propStart = this.peek().range.start;
428
444
  const key = this.consumeObjectKey();
429
445
  this.consume(":");
430
446
  let value;
431
- if (key === "limit") {
432
- const token = this.consumeKind("number", "Expected generate limit");
433
- limit = this.parseBudgetToken(token);
447
+ if (key === "max_output") {
448
+ const token = this.consumeKind("number", "Expected generate max_output");
449
+ maxOutput = this.parseBudgetToken(token);
434
450
  value = {
435
451
  kind: "NumberExpr",
436
452
  value: Number.parseFloat(token.value),
@@ -453,6 +469,15 @@ class Parser {
453
469
  else if (key === "attempts" && value.kind === "NumberExpr") {
454
470
  attempts = value;
455
471
  }
472
+ else if (key === "temperature" && value.kind === "NumberExpr") {
473
+ temperature = value;
474
+ }
475
+ else if (key === "think" && (value.kind === "BooleanExpr" || value.kind === "StringExpr")) {
476
+ think = value;
477
+ }
478
+ else if (key === "strict" && value.kind === "BooleanExpr") {
479
+ strict = value;
480
+ }
456
481
  else if (key === "debug" && value.kind === "BooleanExpr") {
457
482
  debug = value;
458
483
  }
@@ -464,7 +489,10 @@ class Parser {
464
489
  properties,
465
490
  input,
466
491
  attempts,
467
- limit,
492
+ maxOutput,
493
+ temperature,
494
+ think,
495
+ strict,
468
496
  debug,
469
497
  range: { start, end: this.previous().range.end }
470
498
  };
@@ -656,3 +684,15 @@ class Parser {
656
684
  function isImportResourceKind(value) {
657
685
  return IMPORT_RESOURCE_KINDS.has(value);
658
686
  }
687
+ function stringifyLabelTokens(tokens) {
688
+ let label = tokens[0]?.value ?? "";
689
+ for (let index = 1; index < tokens.length; index += 1) {
690
+ const previous = tokens[index - 1];
691
+ const current = tokens[index];
692
+ if (current.range.start.column > previous.range.end.column) {
693
+ label += " ";
694
+ }
695
+ label += current.value;
696
+ }
697
+ return label;
698
+ }
@@ -5,7 +5,7 @@ export async function callAnthropic(request, parsed, options, fetchImpl, timeout
5
5
  if (!apiKey) {
6
6
  throw new RuntimeError("ANTHROPIC_API_KEY is required for anthropic:// models");
7
7
  }
8
- const response = await postJson(fetchImpl, `${baseUrl}/messages`, {
8
+ const body = {
9
9
  model: parsed.model,
10
10
  max_tokens: budgetToTokenLimit(request) ?? 1024,
11
11
  system: request.builtContext.system,
@@ -15,7 +15,11 @@ export async function callAnthropic(request, parsed, options, fetchImpl, timeout
15
15
  content: request.builtContext.finalUserMessage,
16
16
  },
17
17
  ],
18
- }, timeoutMs, {
18
+ };
19
+ if (request.temperature !== undefined) {
20
+ body.temperature = request.temperature;
21
+ }
22
+ const response = await postJson(fetchImpl, `${baseUrl}/messages`, body, timeoutMs, {
19
23
  "x-api-key": apiKey,
20
24
  "anthropic-version": "2023-06-01",
21
25
  });
@@ -3,7 +3,7 @@ export async function callOllama(request, parsed, fetchImpl, timeoutMs, baseUrl)
3
3
  const body = {
4
4
  model: parsed.model,
5
5
  stream: false,
6
- think: false,
6
+ think: request.think ?? false,
7
7
  messages: [
8
8
  { role: "system", content: request.builtContext.system },
9
9
  { role: "user", content: request.builtContext.finalUserMessage },
@@ -13,8 +13,15 @@ export async function callOllama(request, parsed, fetchImpl, timeoutMs, baseUrl)
13
13
  body.format = request.builtContext.returnSchema;
14
14
  }
15
15
  const maxTokens = budgetToTokenLimit(request);
16
+ const options = {};
16
17
  if (maxTokens) {
17
- body.options = { num_predict: maxTokens };
18
+ options.num_predict = maxTokens;
19
+ }
20
+ if (request.temperature !== undefined) {
21
+ options.temperature = request.temperature;
22
+ }
23
+ if (Object.keys(options).length > 0) {
24
+ body.options = options;
18
25
  }
19
26
  const response = await postJson(fetchImpl, `${baseUrl}/api/chat`, body, timeoutMs);
20
27
  const text = readPath(response, ["message", "content"]);
@@ -17,11 +17,18 @@ export async function callOpenAI(request, parsed, options, fetchImpl, timeoutMs,
17
17
  type: "json_schema",
18
18
  json_schema: {
19
19
  name: "agentscript_generate",
20
- strict: true,
20
+ strict: request.strict,
21
21
  schema: request.builtContext.returnSchema,
22
22
  },
23
23
  };
24
24
  }
25
+ if (request.temperature !== undefined) {
26
+ body.temperature = request.temperature;
27
+ }
28
+ const reasoningEffort = openAIReasoningEffort(request.think);
29
+ if (reasoningEffort) {
30
+ body.reasoning_effort = reasoningEffort;
31
+ }
25
32
  const maxTokens = budgetToTokenLimit(request);
26
33
  if (maxTokens) {
27
34
  body.max_completion_tokens = maxTokens;
@@ -32,3 +39,10 @@ export async function callOpenAI(request, parsed, options, fetchImpl, timeoutMs,
32
39
  const text = readPath(response, ["choices", 0, "message", "content"]);
33
40
  return request.returnShape ? parseJsonText(text) : text;
34
41
  }
42
+ function openAIReasoningEffort(think) {
43
+ if (think === true)
44
+ return "medium";
45
+ if (think === "low" || think === "medium" || think === "high")
46
+ return think;
47
+ return undefined;
48
+ }
@@ -1,12 +1,12 @@
1
1
  import { RuntimeError } from "../../runtime/errors.js";
2
2
  export function budgetToTokenLimit(request) {
3
- if (!request.budget) {
3
+ if (!request.maxOutput) {
4
4
  return undefined;
5
5
  }
6
- if (request.budget.unit === "k") {
7
- return Math.max(1, Math.floor(request.budget.amount * 1000));
6
+ if (request.maxOutput.unit === "k") {
7
+ return Math.max(1, Math.floor(request.maxOutput.amount * 1000));
8
8
  }
9
- return Math.max(1, Math.floor(request.budget.amount));
9
+ return Math.max(1, Math.floor(request.maxOutput.amount));
10
10
  }
11
11
  export async function postJson(fetchImpl, url, body, timeoutMs, headers = {}) {
12
12
  const controller = new AbortController();
@@ -14,7 +14,7 @@ export function buildContext(input) {
14
14
  instruction,
15
15
  instructionText,
16
16
  returnSchema,
17
- budget: input.budget,
17
+ maxOutput: input.maxOutput,
18
18
  finalUserMessage: buildFinalUserMessage(context, instructionText, returnSchema),
19
19
  };
20
20
  }
@@ -51,6 +51,7 @@ function buildContextItem(item, index) {
51
51
  return {
52
52
  index,
53
53
  source: item.source,
54
+ label: item.label,
54
55
  value: clipped.value,
55
56
  text: clipped.text,
56
57
  budget: item.budget,
@@ -60,8 +61,15 @@ function buildContextItem(item, index) {
60
61
  };
61
62
  }
62
63
  function buildSystemPrompt(agentName, identity) {
63
- const lines = [`You are ${agentName}.`];
64
+ const role = typeof identity.role === "string" ? identity.role : agentName;
65
+ const lines = [`You are ${role}.`];
66
+ if (typeof identity.description === "string") {
67
+ lines.push(identity.description);
68
+ }
64
69
  for (const [key, value] of Object.entries(identity)) {
70
+ if (key === "role" || key === "description") {
71
+ continue;
72
+ }
65
73
  lines.push(`${key}: ${renderJson(value)}`);
66
74
  }
67
75
  return lines.join("\n");
@@ -71,8 +79,9 @@ function buildFinalUserMessage(context, instructionText, returnSchema) {
71
79
  if (context.length > 0) {
72
80
  sections.push("Context:");
73
81
  for (const item of context) {
74
- const label = item.source ? `${item.source}:\n` : "";
75
- sections.push(`[${item.index}] ${label}${item.text}`);
82
+ const label = item.label ?? String(item.index);
83
+ const source = item.source ? `source: ${item.source}\n` : "";
84
+ sections.push(`[${label}]\n${source}${item.text}`);
76
85
  }
77
86
  }
78
87
  sections.push("Instruction:", instructionText);
@@ -147,6 +156,7 @@ export function builtContextToJson(context) {
147
156
  context: context.context.map((item) => ({
148
157
  index: item.index,
149
158
  source: item.source ?? null,
159
+ label: item.label ?? null,
150
160
  value: item.value,
151
161
  text: item.text,
152
162
  budget: budgetToJson(item.budget),
@@ -156,7 +166,7 @@ export function builtContextToJson(context) {
156
166
  })),
157
167
  instruction: context.instruction,
158
168
  returnSchema: context.returnSchema ?? null,
159
- budget: budgetToJson(context.budget),
169
+ maxOutput: budgetToJson(context.maxOutput),
160
170
  finalUserMessage: context.finalUserMessage,
161
171
  };
162
172
  }
@@ -80,6 +80,7 @@ export class Evaluator {
80
80
  for (const item of scope.visibleUses()) {
81
81
  uses.push({
82
82
  source: item.source,
83
+ label: item.label,
83
84
  value: await this.evaluate(item.expr, item.scope),
84
85
  budget: item.budget,
85
86
  });
@@ -29,7 +29,7 @@ export class GenerateRuntime {
29
29
  instruction,
30
30
  returnShape: expr.returnShape,
31
31
  uses: context,
32
- budget: options.limit,
32
+ maxOutput: options.maxOutput,
33
33
  });
34
34
  if (options.debug) {
35
35
  writeDebugPrompt(agent.name, attempt, builtContext);
@@ -44,7 +44,11 @@ export class GenerateRuntime {
44
44
  returnShape: expr.returnShape,
45
45
  context,
46
46
  builtContext,
47
- budget: options.limit,
47
+ maxOutput: options.maxOutput,
48
+ temperature: options.temperature,
49
+ think: options.think,
50
+ strict: options.strict,
51
+ debug: options.debug,
48
52
  });
49
53
  }
50
54
  catch (error) {
@@ -58,18 +62,27 @@ export class GenerateRuntime {
58
62
  continue;
59
63
  }
60
64
  try {
61
- const result = expr.returnShape ? coerceValueToShape(rawResult, expr.returnShape) : rawResult;
65
+ const result = expr.returnShape && !options.strict ? coerceValueToShape(rawResult, expr.returnShape) : rawResult;
62
66
  if (expr.returnShape) {
63
- validateValueAgainstShape(result, expr.returnShape, expr.range);
67
+ validateValueAgainstShape(result, expr.returnShape, expr.range, { rejectExtraFields: options.strict });
64
68
  }
65
69
  this.trace.push({
66
70
  kind: "generate",
67
71
  data: {
68
72
  instruction: sanitizeForJson(options.input),
73
+ config: {
74
+ maxOutput: budgetToJson(options.maxOutput),
75
+ attempts: options.attempts,
76
+ temperature: options.temperature ?? null,
77
+ think: options.think ?? false,
78
+ strict: options.strict,
79
+ debug: options.debug,
80
+ },
69
81
  attempts: attempt,
70
- budget: budgetToJson(options.limit),
82
+ maxOutput: budgetToJson(options.maxOutput),
71
83
  debug: options.debug,
72
84
  context: builtContextToJson(builtContext),
85
+ validation: expr.returnShape ? { ok: true, strict: options.strict } : null,
73
86
  result: sanitizeForJson(result),
74
87
  },
75
88
  });
@@ -99,7 +112,10 @@ export class GenerateRuntime {
99
112
  return {
100
113
  input: await this.host.evaluate(expr.options.input, scope),
101
114
  attempts,
102
- limit: expr.options.limit,
115
+ maxOutput: expr.options.maxOutput,
116
+ temperature: expr.options.temperature?.value,
117
+ think: expr.options.think?.value,
118
+ strict: expr.options.strict?.value ?? false,
103
119
  debug: expr.options.debug?.value ?? false,
104
120
  };
105
121
  }
@@ -209,8 +209,11 @@ class Interpreter {
209
209
  return undefined;
210
210
  case "UseStmt": {
211
211
  const source = formatExpressionSource(stmt.value);
212
- scope.addUse(stmt.value, source, stmt.budget);
213
- this.trace.push({ kind: "use", data: { source, budget: budgetToJson(stmt.budget) } });
212
+ scope.addUse(stmt.value, source, stmt.budget, stmt.label);
213
+ this.trace.push({
214
+ kind: "use",
215
+ data: { source, label: stmt.label ?? null, budget: budgetToJson(stmt.budget) },
216
+ });
214
217
  return undefined;
215
218
  }
216
219
  case "AssignStmt": {
@@ -29,8 +29,8 @@ export class RuntimeScope {
29
29
  }
30
30
  this.define(name, value);
31
31
  }
32
- addUse(expr, source, budget) {
33
- this.uses.push({ expr, source, budget, scope: this });
32
+ addUse(expr, source, budget, label) {
33
+ this.uses.push({ expr, source, budget, label, scope: this });
34
34
  }
35
35
  setConfig(name, value) {
36
36
  this.config.set(name, value);
@@ -7,14 +7,16 @@ export function buildValueFromShape(shape) {
7
7
  }
8
8
  return result;
9
9
  }
10
- export function validateValueAgainstShape(value, shape, range) {
10
+ export function validateValueAgainstShape(value, shape, range, options = {}) {
11
11
  if (!isObject(value)) {
12
12
  throw new RuntimeError("LLM result must be an object matching the generate return shape", range);
13
13
  }
14
14
  const allowedFields = new Set(shape.fields.map((field) => field.name));
15
- for (const key of Object.keys(value)) {
16
- if (!allowedFields.has(key)) {
17
- throw new RuntimeError(`LLM result contains unexpected field '${key}'`, range);
15
+ if (options.rejectExtraFields) {
16
+ for (const key of Object.keys(value)) {
17
+ if (!allowedFields.has(key)) {
18
+ throw new RuntimeError(`LLM result contains unexpected field '${key}'`, range);
19
+ }
18
20
  }
19
21
  }
20
22
  for (const field of shape.fields) {
@@ -171,6 +171,9 @@ class Analyzer {
171
171
  if (stmt.budget && stmt.budget.amount <= 0) {
172
172
  this.error("INVALID_BUDGET", "Budget amount must be greater than 0", stmt.range);
173
173
  }
174
+ if (stmt.label && RESERVED_CONTEXT_LABELS.has(stmt.label)) {
175
+ this.error("RESERVED_CONTEXT_LABEL", `Context label '${stmt.label}' is reserved`, stmt.range);
176
+ }
174
177
  break;
175
178
  case "AssignStmt":
176
179
  this.checkAssignment(stmt, scope);
@@ -392,9 +395,26 @@ class Analyzer {
392
395
  this.error("INVALID_GENERATE_ATTEMPTS", "generate attempts must be a positive integer", property.value.range);
393
396
  }
394
397
  }
395
- else if (property.key === "limit") {
396
- if (!expr.options.limit || expr.options.limit.amount <= 0) {
397
- this.error("INVALID_GENERATE_LIMIT", "generate limit must be a positive budget", property.value.range);
398
+ else if (property.key === "max_output") {
399
+ if (!expr.options.maxOutput || expr.options.maxOutput.amount <= 0) {
400
+ this.error("INVALID_GENERATE_MAX_OUTPUT", "generate max_output must be a positive budget", property.value.range);
401
+ }
402
+ }
403
+ else if (property.key === "temperature") {
404
+ if (property.value.kind !== "NumberExpr") {
405
+ this.error("INVALID_GENERATE_TEMPERATURE", "generate temperature must be a number", property.value.range);
406
+ }
407
+ }
408
+ else if (property.key === "think") {
409
+ const validThinkString = property.value.kind === "StringExpr" &&
410
+ ["auto", "low", "medium", "high"].includes(property.value.value);
411
+ if (property.value.kind !== "BooleanExpr" && !validThinkString) {
412
+ this.error("INVALID_GENERATE_THINK", "generate think must be a boolean or one of auto, low, medium, high", property.value.range);
413
+ }
414
+ }
415
+ else if (property.key === "strict") {
416
+ if (property.value.kind !== "BooleanExpr") {
417
+ this.error("INVALID_GENERATE_STRICT", "generate strict must be a boolean", property.value.range);
398
418
  }
399
419
  }
400
420
  else if (property.key === "debug") {
@@ -501,6 +521,7 @@ class Analyzer {
501
521
  const IMPORTED_BINDING_KINDS = new Set(["tool", "llm", "file", "agent", "memory"]);
502
522
  const NON_CONTEXT_BINDING_KINDS = new Set(["tool", "llm", "agent", "function", "memory"]);
503
523
  const VALID_MEMORY_METHODS = new Set(["add", "query"]);
524
+ const RESERVED_CONTEXT_LABELS = new Set(["system", "assistant", "tool", "developer"]);
504
525
  function functionBinding(agentName, fn) {
505
526
  return {
506
527
  kind: "function",