@prompty/core 2.0.0-alpha.5 → 2.0.0-alpha.7

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/dist/index.js CHANGED
@@ -2220,7 +2220,12 @@ var init_property = __esm({
2220
2220
  for (const [key, value] of Object.entries(
2221
2221
  data
2222
2222
  )) {
2223
- if (value && typeof value === "object" && !Array.isArray(value)) {
2223
+ if (Array.isArray(value)) {
2224
+ throw new Error(
2225
+ `Invalid 'properties' format: key '${key}' has an array value. 'properties' must be a flat list of objects or a name-keyed dict \u2014 not a nested { ${key}: [...] } structure.`
2226
+ );
2227
+ }
2228
+ if (value && typeof value === "object") {
2224
2229
  value["name"] = key;
2225
2230
  result.push(Property.load(value, context));
2226
2231
  } else {
@@ -3128,7 +3133,12 @@ var init_tool = __esm({
3128
3133
  for (const [key, value] of Object.entries(
3129
3134
  data
3130
3135
  )) {
3131
- if (value && typeof value === "object" && !Array.isArray(value)) {
3136
+ if (Array.isArray(value)) {
3137
+ throw new Error(
3138
+ `Invalid 'bindings' format: key '${key}' has an array value. 'bindings' must be a flat list of objects or a name-keyed dict \u2014 not a nested { ${key}: [...] } structure.`
3139
+ );
3140
+ }
3141
+ if (value && typeof value === "object") {
3132
3142
  value["name"] = key;
3133
3143
  result.push(Binding.load(value, context));
3134
3144
  } else {
@@ -3343,7 +3353,12 @@ var init_tool = __esm({
3343
3353
  for (const [key, value] of Object.entries(
3344
3354
  data
3345
3355
  )) {
3346
- if (value && typeof value === "object" && !Array.isArray(value)) {
3356
+ if (Array.isArray(value)) {
3357
+ throw new Error(
3358
+ `Invalid 'parameters' format: key '${key}' has an array value. 'parameters' must be a flat list of objects or a name-keyed dict \u2014 not a nested { ${key}: [...] } structure.`
3359
+ );
3360
+ }
3361
+ if (value && typeof value === "object") {
3347
3362
  value["name"] = key;
3348
3363
  result.push(Property.load(value, context));
3349
3364
  } else {
@@ -4100,7 +4115,12 @@ var init_prompty = __esm({
4100
4115
  for (const [key, value] of Object.entries(
4101
4116
  data
4102
4117
  )) {
4103
- if (value && typeof value === "object" && !Array.isArray(value)) {
4118
+ if (Array.isArray(value)) {
4119
+ throw new Error(
4120
+ `Invalid 'inputs' format: key '${key}' has an array value. 'inputs' must be a flat list of objects or a name-keyed dict \u2014 not a nested { ${key}: [...] } structure.`
4121
+ );
4122
+ }
4123
+ if (value && typeof value === "object") {
4104
4124
  value["name"] = key;
4105
4125
  result.push(Property.load(value, context));
4106
4126
  } else {
@@ -4130,7 +4150,12 @@ var init_prompty = __esm({
4130
4150
  for (const [key, value] of Object.entries(
4131
4151
  data
4132
4152
  )) {
4133
- if (value && typeof value === "object" && !Array.isArray(value)) {
4153
+ if (Array.isArray(value)) {
4154
+ throw new Error(
4155
+ `Invalid 'outputs' format: key '${key}' has an array value. 'outputs' must be a flat list of objects or a name-keyed dict \u2014 not a nested { ${key}: [...] } structure.`
4156
+ );
4157
+ }
4158
+ if (value && typeof value === "object") {
4134
4159
  value["name"] = key;
4135
4160
  result.push(Property.load(value, context));
4136
4161
  } else {
@@ -4160,7 +4185,12 @@ var init_prompty = __esm({
4160
4185
  for (const [key, value] of Object.entries(
4161
4186
  data
4162
4187
  )) {
4163
- if (value && typeof value === "object" && !Array.isArray(value)) {
4188
+ if (Array.isArray(value)) {
4189
+ throw new Error(
4190
+ `Invalid 'tools' format: key '${key}' has an array value. 'tools' must be a flat list of objects or a name-keyed dict \u2014 not a nested { ${key}: [...] } structure.`
4191
+ );
4192
+ }
4193
+ if (value && typeof value === "object") {
4164
4194
  value["name"] = key;
4165
4195
  result.push(Tool.load(value, context));
4166
4196
  } else {
@@ -4499,6 +4529,9 @@ var init_common = __esm({
4499
4529
 
4500
4530
  // src/core/tool-dispatch.ts
4501
4531
  import { dirname as dirname2, resolve as resolve2 } from "path";
4532
+ function registerTool(name, handler) {
4533
+ nameRegistry.set(name, handler);
4534
+ }
4502
4535
  function getTool(name) {
4503
4536
  return nameRegistry.get(name);
4504
4537
  }
@@ -4522,12 +4555,12 @@ async function dispatchTool(toolName, args, userTools, agent, parentInputs) {
4522
4555
  const result = await registeredFn(args);
4523
4556
  return typeof result === "string" ? result : JSON.stringify(result);
4524
4557
  }
4525
- const tool = agent.tools?.find((t) => t.name === toolName);
4526
- if (!tool) {
4558
+ const tool2 = agent.tools?.find((t) => t.name === toolName);
4559
+ if (!tool2) {
4527
4560
  const available = Object.keys(userTools).sort().join(", ") || "(none)";
4528
4561
  return `Error: tool "${toolName}" not found in userTools or agent.tools. Available user tools: ${available}`;
4529
4562
  }
4530
- const kind = tool.kind || "*";
4563
+ const kind = tool2.kind || "*";
4531
4564
  let handler;
4532
4565
  try {
4533
4566
  handler = getToolHandler(kind);
@@ -4539,7 +4572,7 @@ async function dispatchTool(toolName, args, userTools, agent, parentInputs) {
4539
4572
  }
4540
4573
  }
4541
4574
  return await handler.executeTool(
4542
- tool,
4575
+ tool2,
4543
4576
  args,
4544
4577
  agent,
4545
4578
  parentInputs
@@ -4564,36 +4597,36 @@ var init_tool_dispatch = __esm({
4564
4597
  nameRegistry = /* @__PURE__ */ new Map();
4565
4598
  toolHandlers = /* @__PURE__ */ new Map();
4566
4599
  FunctionToolHandler = class {
4567
- async executeTool(tool, _args, _agent, _parentInputs) {
4568
- const name = tool.name ?? "unknown";
4600
+ async executeTool(tool2, _args, _agent, _parentInputs) {
4601
+ const name = tool2.name ?? "unknown";
4569
4602
  throw new Error(
4570
- `Function tool '${name}' declared but no callable provided. Pass it via tools: { '${name}': fn } in invokeAgent().`
4603
+ `Function tool '${name}' declared but no callable provided. Pass it via tools: { '${name}': fn } in turn().`
4571
4604
  );
4572
4605
  }
4573
4606
  };
4574
4607
  PromptyToolHandler = class {
4575
- async executeTool(tool, args, agent, _parentInputs) {
4608
+ async executeTool(tool2, args, agent, _parentInputs) {
4576
4609
  const { load: load2 } = await Promise.resolve().then(() => (init_loader(), loader_exports));
4577
- const { prepare: prepare2, run: run2, invokeAgent: invokeAgent2 } = await Promise.resolve().then(() => (init_pipeline(), pipeline_exports));
4610
+ const { prepare: prepare2, run: run2, turn: turn2 } = await Promise.resolve().then(() => (init_pipeline(), pipeline_exports));
4578
4611
  const parentPath = (agent.metadata ?? {}).__source_path;
4579
4612
  if (!parentPath) {
4580
- return `Error: cannot resolve PromptyTool '${tool.name}': parent has no __source_path`;
4613
+ return `Error: cannot resolve PromptyTool '${tool2.name}': parent has no __source_path`;
4581
4614
  }
4582
- const childPath = resolve2(dirname2(parentPath), tool.path);
4615
+ const childPath = resolve2(dirname2(parentPath), tool2.path);
4583
4616
  const stack = (agent.metadata ?? {}).__prompty_tool_stack ?? [];
4584
4617
  const normalizedChild = resolve2(childPath);
4585
4618
  const visited = /* @__PURE__ */ new Set([...stack.map((p) => resolve2(p)), resolve2(parentPath)]);
4586
4619
  if (visited.has(normalizedChild)) {
4587
4620
  const chain = [...stack, parentPath, childPath].join(" \u2192 ");
4588
- return `Error executing PromptyTool '${tool.name}': circular reference detected: ${chain}`;
4621
+ return `Error executing PromptyTool '${tool2.name}': circular reference detected: ${chain}`;
4589
4622
  }
4590
4623
  try {
4591
4624
  const child = load2(childPath);
4592
4625
  if (!child.metadata) child.metadata = {};
4593
4626
  child.metadata.__prompty_tool_stack = [...stack, parentPath];
4594
- const mode = tool.mode ?? "single";
4627
+ const mode = tool2.mode ?? "single";
4595
4628
  if (mode === "agentic") {
4596
- const result = await invokeAgent2(child, args);
4629
+ const result = await turn2(child, args);
4597
4630
  return typeof result === "string" ? result : JSON.stringify(result);
4598
4631
  } else {
4599
4632
  const messages = await prepare2(child, args);
@@ -4601,7 +4634,7 @@ var init_tool_dispatch = __esm({
4601
4634
  return typeof result === "string" ? result : JSON.stringify(result);
4602
4635
  }
4603
4636
  } catch (err) {
4604
- return `Error executing PromptyTool '${tool.name}': ${err instanceof Error ? err.message : String(err)}`;
4637
+ return `Error executing PromptyTool '${tool2.name}': ${err instanceof Error ? err.message : String(err)}`;
4605
4638
  }
4606
4639
  }
4607
4640
  };
@@ -4628,6 +4661,205 @@ var init_tool_dispatch = __esm({
4628
4661
  }
4629
4662
  });
4630
4663
 
4664
+ // src/core/agent-events.ts
4665
+ function emitEvent(callback, eventType, data) {
4666
+ if (!callback) return;
4667
+ try {
4668
+ callback(eventType, data);
4669
+ } catch (err) {
4670
+ if (typeof globalThis.console?.debug === "function") {
4671
+ globalThis.console.debug(`Event callback error for ${eventType}:`, err);
4672
+ }
4673
+ }
4674
+ }
4675
+ var init_agent_events = __esm({
4676
+ "src/core/agent-events.ts"() {
4677
+ "use strict";
4678
+ }
4679
+ });
4680
+
4681
+ // src/core/cancellation.ts
4682
+ function checkCancellation(signal) {
4683
+ if (signal?.aborted) {
4684
+ throw new CancelledError();
4685
+ }
4686
+ }
4687
+ var CancelledError;
4688
+ var init_cancellation = __esm({
4689
+ "src/core/cancellation.ts"() {
4690
+ "use strict";
4691
+ CancelledError = class extends Error {
4692
+ constructor(message = "Agent loop cancelled") {
4693
+ super(message);
4694
+ this.name = "CancelledError";
4695
+ }
4696
+ };
4697
+ }
4698
+ });
4699
+
4700
+ // src/core/context.ts
4701
+ function estimateChars(messages) {
4702
+ let total = 0;
4703
+ for (const msg of messages) {
4704
+ total += msg.role.length + 4;
4705
+ for (const part of msg.parts) {
4706
+ if (part.kind === "text") {
4707
+ total += part.value.length;
4708
+ } else {
4709
+ total += 200;
4710
+ }
4711
+ }
4712
+ const toolCalls = msg.metadata?.tool_calls;
4713
+ if (toolCalls) {
4714
+ total += JSON.stringify(toolCalls).length;
4715
+ }
4716
+ }
4717
+ return total;
4718
+ }
4719
+ function truncate(text2, maxLen = 200) {
4720
+ return text2.length <= maxLen ? text2 : text2.slice(0, maxLen) + "\u2026";
4721
+ }
4722
+ function summarizeDropped(messages) {
4723
+ const lines = [];
4724
+ for (const msg of messages) {
4725
+ const msgText = msg.text.trim();
4726
+ if (msg.role === "user" && msgText) {
4727
+ lines.push(`User asked: ${truncate(msgText)}`);
4728
+ } else if (msg.role === "assistant") {
4729
+ if (msgText) lines.push(`Assistant: ${truncate(msgText)}`);
4730
+ const toolCalls = msg.metadata?.tool_calls;
4731
+ if (Array.isArray(toolCalls)) {
4732
+ const names = toolCalls.map(
4733
+ (tc) => tc.name ?? (tc.function?.name ?? "?")
4734
+ );
4735
+ lines.push(` Called tools: ${names.join(", ")}`);
4736
+ }
4737
+ }
4738
+ }
4739
+ if (lines.length === 0) return "";
4740
+ let result = "[Context summary: ";
4741
+ for (const line of lines) {
4742
+ if (result.length + line.length > 4e3) {
4743
+ result += "\n... (older messages omitted)";
4744
+ break;
4745
+ }
4746
+ result += line + "\n";
4747
+ }
4748
+ return result.trimEnd() + "]";
4749
+ }
4750
+ function trimToContextWindow(messages, budgetChars) {
4751
+ if (estimateChars(messages) <= budgetChars) {
4752
+ return [0, []];
4753
+ }
4754
+ let systemEnd = 0;
4755
+ for (let i = 0; i < messages.length; i++) {
4756
+ if (messages[i].role !== "system") {
4757
+ systemEnd = i;
4758
+ break;
4759
+ }
4760
+ if (i === messages.length - 1) systemEnd = messages.length;
4761
+ }
4762
+ const systemMsgs = messages.slice(0, systemEnd);
4763
+ const rest = messages.slice(systemEnd);
4764
+ const summaryBudget = Math.min(5e3, Math.floor(budgetChars * 0.05));
4765
+ const dropped = [];
4766
+ while (estimateChars([...systemMsgs, ...rest]) > budgetChars - summaryBudget && rest.length > 2) {
4767
+ dropped.push(rest.shift());
4768
+ }
4769
+ const droppedCount = dropped.length;
4770
+ messages.length = 0;
4771
+ messages.push(...systemMsgs);
4772
+ if (droppedCount > 0) {
4773
+ const summaryText = summarizeDropped(dropped);
4774
+ if (summaryText) {
4775
+ messages.push(new Message("user", [{ kind: "text", value: summaryText }]));
4776
+ }
4777
+ }
4778
+ messages.push(...rest);
4779
+ return [droppedCount, dropped];
4780
+ }
4781
+ var init_context2 = __esm({
4782
+ "src/core/context.ts"() {
4783
+ "use strict";
4784
+ init_types();
4785
+ }
4786
+ });
4787
+
4788
+ // src/core/guardrails.ts
4789
+ var GuardrailError, Guardrails;
4790
+ var init_guardrails = __esm({
4791
+ "src/core/guardrails.ts"() {
4792
+ "use strict";
4793
+ GuardrailError = class extends Error {
4794
+ reason;
4795
+ constructor(reason) {
4796
+ super(`Guardrail denied: ${reason}`);
4797
+ this.name = "GuardrailError";
4798
+ this.reason = reason;
4799
+ }
4800
+ };
4801
+ Guardrails = class {
4802
+ inputHook;
4803
+ outputHook;
4804
+ toolHook;
4805
+ constructor(options) {
4806
+ this.inputHook = options?.input;
4807
+ this.outputHook = options?.output;
4808
+ this.toolHook = options?.tool;
4809
+ }
4810
+ checkInput(messages) {
4811
+ if (!this.inputHook) return { allowed: true };
4812
+ return this.inputHook(messages);
4813
+ }
4814
+ checkOutput(message) {
4815
+ if (!this.outputHook) return { allowed: true };
4816
+ return this.outputHook(message);
4817
+ }
4818
+ checkTool(name, args) {
4819
+ if (!this.toolHook) return { allowed: true };
4820
+ return this.toolHook(name, args);
4821
+ }
4822
+ };
4823
+ }
4824
+ });
4825
+
4826
+ // src/core/structured.ts
4827
+ function createStructuredResult(data, rawJson) {
4828
+ const result = { ...data };
4829
+ Object.defineProperty(result, StructuredResultSymbol, {
4830
+ value: rawJson,
4831
+ writable: false,
4832
+ enumerable: false,
4833
+ configurable: false
4834
+ });
4835
+ return result;
4836
+ }
4837
+ function isStructuredResult(value) {
4838
+ return typeof value === "object" && value !== null && StructuredResultSymbol in value;
4839
+ }
4840
+ function cast(result, validator) {
4841
+ let jsonStr;
4842
+ if (isStructuredResult(result)) {
4843
+ jsonStr = result[StructuredResultSymbol];
4844
+ } else if (typeof result === "string") {
4845
+ jsonStr = result;
4846
+ } else {
4847
+ jsonStr = JSON.stringify(result);
4848
+ }
4849
+ const parsed = JSON.parse(jsonStr);
4850
+ if (validator) {
4851
+ return validator(parsed);
4852
+ }
4853
+ return parsed;
4854
+ }
4855
+ var StructuredResultSymbol;
4856
+ var init_structured = __esm({
4857
+ "src/core/structured.ts"() {
4858
+ "use strict";
4859
+ StructuredResultSymbol = /* @__PURE__ */ Symbol("prompty.rawJson");
4860
+ }
4861
+ });
4862
+
4631
4863
  // src/core/pipeline.ts
4632
4864
  var pipeline_exports = {};
4633
4865
  __export(pipeline_exports, {
@@ -4639,6 +4871,7 @@ __export(pipeline_exports, {
4639
4871
  render: () => render,
4640
4872
  resolveBindings: () => resolveBindings,
4641
4873
  run: () => run,
4874
+ turn: () => turn,
4642
4875
  validateInputs: () => validateInputs
4643
4876
  });
4644
4877
  function sanitizeNonces(value) {
@@ -4829,129 +5062,27 @@ async function invoke(prompt, inputs, options) {
4829
5062
  emit("description", "Invoke a prompty");
4830
5063
  emit("inputs", { prompt: serializeAgent(agent), inputs: inputs ?? {} });
4831
5064
  const messages = await prepare(agent, inputs);
4832
- const result = await run(agent, messages, options);
4833
- emit("result", result);
4834
- return result;
4835
- });
4836
- }
4837
- function resolveBindings(agent, toolName, args, parentInputs) {
4838
- if (!parentInputs || !agent.tools || agent.tools.length === 0) return args;
4839
- const toolDef = agent.tools.find((t) => t.name === toolName);
4840
- if (!toolDef || !toolDef.bindings || toolDef.bindings.length === 0) return args;
4841
- const merged = { ...args };
4842
- for (const binding of toolDef.bindings) {
4843
- if (binding.input in parentInputs) {
4844
- merged[binding.name] = parentInputs[binding.input];
4845
- }
4846
- }
4847
- return merged;
4848
- }
4849
- function isAsyncIterable(value) {
4850
- return value != null && typeof value === "object" && Symbol.asyncIterator in value;
4851
- }
4852
- function isToolCallLike(item) {
4853
- return typeof item === "object" && item !== null && "id" in item && "name" in item && "arguments" in item;
4854
- }
4855
- async function consumeStream(agent, response) {
4856
- const processed = await process2(agent, response);
4857
- const toolCalls = [];
4858
- const textParts = [];
4859
- if (isAsyncIterable(processed)) {
4860
- for await (const item of processed) {
4861
- if (isToolCallLike(item)) {
4862
- toolCalls.push(item);
4863
- } else if (typeof item === "string") {
4864
- textParts.push(item);
5065
+ const provider = resolveProvider(agent);
5066
+ const executor = getExecutor(provider);
5067
+ const response = await executor.execute(agent, messages);
5068
+ if (options?.raw) {
5069
+ emit("result", response);
5070
+ if (options?.validator) {
5071
+ return cast(response, options.validator);
4865
5072
  }
5073
+ return response;
4866
5074
  }
4867
- } else if (typeof processed === "string") {
4868
- textParts.push(processed);
4869
- }
4870
- return { toolCalls, content: textParts.join("") };
4871
- }
4872
- async function buildToolMessagesFromCalls(toolCalls, textContent, tools, agent, parentInputs, parentEmit) {
4873
- const provider = resolveProvider(agent);
4874
- const apiType = agent.model?.apiType || "chat";
4875
- const messages = [];
4876
- const toolInputs = [];
4877
- if (provider === "anthropic") {
4878
- const rawContent = [];
4879
- if (textContent) rawContent.push({ type: "text", text: textContent });
4880
- for (const tc of toolCalls) {
4881
- rawContent.push({
4882
- type: "tool_use",
4883
- id: tc.id,
4884
- name: tc.name,
4885
- input: JSON.parse(tc.arguments)
4886
- });
4887
- }
4888
- messages.push(
4889
- new Message("assistant", textContent ? [text(textContent)] : [], { content: rawContent })
4890
- );
4891
- } else if (apiType === "responses") {
4892
- for (const tc of toolCalls) {
4893
- messages.push(
4894
- new Message("assistant", [], {
4895
- responses_function_call: {
4896
- type: "function_call",
4897
- call_id: tc.id,
4898
- name: tc.name,
4899
- arguments: tc.arguments
4900
- }
4901
- })
4902
- );
4903
- }
4904
- } else {
4905
- const rawToolCalls = toolCalls.map((tc) => ({
4906
- id: tc.id,
4907
- type: "function",
4908
- function: { name: tc.name, arguments: tc.arguments }
4909
- }));
4910
- messages.push(
4911
- new Message("assistant", textContent ? [text(textContent)] : [], {
4912
- tool_calls: rawToolCalls
4913
- })
4914
- );
4915
- }
4916
- const toolResultBlocks = [];
4917
- for (const tc of toolCalls) {
4918
- let result;
4919
- let parsedArgs;
4920
- try {
4921
- parsedArgs = JSON.parse(tc.arguments);
4922
- if (parentInputs && typeof parsedArgs === "object" && parsedArgs !== null && !Array.isArray(parsedArgs)) {
4923
- parsedArgs = resolveBindings(agent, tc.name, parsedArgs, parentInputs);
4924
- }
4925
- result = await traceSpan(tc.name, async (toolEmit) => {
4926
- toolEmit("signature", `prompty.tool.${tc.name}`);
4927
- toolEmit("description", `Execute tool: ${tc.name}`);
4928
- toolEmit("inputs", { arguments: parsedArgs, id: tc.id });
4929
- const r = await dispatchTool(tc.name, parsedArgs, tools, agent, parentInputs ?? {});
4930
- toolEmit("result", r);
4931
- return r;
4932
- });
4933
- } catch (err) {
4934
- result = `Error: ${err instanceof Error ? err.message : String(err)}`;
4935
- }
4936
- toolInputs.push({ name: tc.name, arguments: parsedArgs, id: tc.id, result });
4937
- if (provider === "anthropic") {
4938
- toolResultBlocks.push({ type: "tool_result", tool_use_id: tc.id, content: result });
4939
- } else {
4940
- messages.push(
4941
- new Message("tool", [text(result)], { tool_call_id: tc.id, name: tc.name })
4942
- );
5075
+ const result = await process2(agent, response);
5076
+ emit("result", result);
5077
+ if (options?.validator) {
5078
+ return cast(result, options.validator);
4943
5079
  }
4944
- }
4945
- if (provider === "anthropic" && toolResultBlocks.length > 0) {
4946
- messages.push(new Message("user", [], { tool_results: toolResultBlocks }));
4947
- }
4948
- if (parentEmit) {
4949
- parentEmit("inputs", { tool_calls: toolInputs });
4950
- }
4951
- return messages;
5080
+ return result;
5081
+ });
4952
5082
  }
4953
- async function invokeAgent(prompt, inputs, options) {
4954
- return traceSpan("invokeAgent", async (emit) => {
5083
+ async function turn(prompt, inputs, options) {
5084
+ const label = options?.turn != null ? `turn ${options.turn}` : "turn";
5085
+ const rawResult = await traceSpan(label, async (emit) => {
4955
5086
  const agent = typeof prompt === "string" ? await traceSpan("load", async (loadEmit) => {
4956
5087
  loadEmit("signature", "prompty.load");
4957
5088
  loadEmit("description", "Load a prompty file.");
@@ -4960,23 +5091,126 @@ async function invokeAgent(prompt, inputs, options) {
4960
5091
  loadEmit("result", serializeAgent(loaded));
4961
5092
  return loaded;
4962
5093
  }) : prompt;
5094
+ emit("signature", "prompty.turn");
5095
+ emit("description", label);
5096
+ emit("inputs", sanitizeValue("inputs", inputs));
4963
5097
  const tools = options?.tools ?? {};
5098
+ const hasTools = Object.keys(tools).length > 0;
5099
+ if (!hasTools) {
5100
+ let messages2 = await prepare(agent, inputs);
5101
+ const onEvent2 = options?.onEvent;
5102
+ if (options?.steering) {
5103
+ const pending = options.steering.drain();
5104
+ if (pending.length > 0) {
5105
+ messages2.push(...pending);
5106
+ emitEvent(onEvent2, "messages_updated", { messages: messages2 });
5107
+ }
5108
+ }
5109
+ if (options?.contextBudget !== void 0) {
5110
+ const [droppedCount] = trimToContextWindow(messages2, options.contextBudget);
5111
+ if (droppedCount > 0) {
5112
+ emitEvent(onEvent2, "messages_updated", { messages: messages2 });
5113
+ }
5114
+ }
5115
+ if (options?.guardrails) {
5116
+ const result = options.guardrails.checkInput(messages2);
5117
+ if (!result.allowed) {
5118
+ emitEvent(onEvent2, "error", { message: `Input guardrail denied: ${result.reason}` });
5119
+ throw new GuardrailError(result.reason ?? "Input guardrail denied");
5120
+ }
5121
+ if (result.rewrite) messages2 = result.rewrite;
5122
+ }
5123
+ checkCancellation(options?.signal);
5124
+ const provider2 = resolveProvider(agent);
5125
+ const executor2 = getExecutor(provider2);
5126
+ const response2 = await executor2.execute(agent, messages2);
5127
+ if (options?.raw) {
5128
+ emit("result", response2);
5129
+ return response2;
5130
+ }
5131
+ const processed = await process2(agent, response2);
5132
+ if (options?.guardrails) {
5133
+ const contentStr = typeof processed === "string" ? processed : JSON.stringify(processed);
5134
+ const assistantMsg = new Message("assistant", [text(contentStr)]);
5135
+ const gr = options.guardrails.checkOutput(assistantMsg);
5136
+ if (!gr.allowed) {
5137
+ emitEvent(onEvent2, "error", { message: `Output guardrail denied: ${gr.reason}` });
5138
+ throw new GuardrailError(gr.reason ?? "Output guardrail denied");
5139
+ }
5140
+ if (gr.rewrite !== void 0) {
5141
+ emit("result", gr.rewrite);
5142
+ emitEvent(onEvent2, "done", { response: gr.rewrite, messages: messages2 });
5143
+ return gr.rewrite;
5144
+ }
5145
+ }
5146
+ emit("result", sanitizeValue("result", processed));
5147
+ emitEvent(onEvent2, "done", { response: processed, messages: messages2 });
5148
+ return processed;
5149
+ }
4964
5150
  const maxIterations = options?.maxIterations ?? DEFAULT_MAX_ITERATIONS;
4965
- emit("signature", "prompty.invokeAgent");
4966
- emit("description", "Invoke a prompty with tool calling");
4967
- emit("inputs", { prompt: serializeAgent(agent), tools: Object.keys(tools), inputs: inputs ?? {} });
4968
- const messages = await prepare(agent, inputs);
5151
+ const onEvent = options?.onEvent;
5152
+ const signal = options?.signal;
5153
+ const contextBudget = options?.contextBudget;
5154
+ const guardrails = options?.guardrails;
5155
+ const steering = options?.steering;
5156
+ const parallelToolCalls = options?.parallelToolCalls ?? false;
5157
+ let messages = await prepare(agent, inputs);
4969
5158
  const parentInputs = inputs ?? {};
4970
5159
  const provider = resolveProvider(agent);
4971
5160
  const executor = getExecutor(provider);
4972
- let response = await executor.execute(agent, messages);
5161
+ let response = null;
4973
5162
  let iteration = 0;
4974
5163
  while (true) {
5164
+ try {
5165
+ checkCancellation(signal);
5166
+ } catch (err) {
5167
+ emitEvent(onEvent, "cancelled", {});
5168
+ throw err;
5169
+ }
5170
+ if (steering) {
5171
+ const pending = steering.drain();
5172
+ if (pending.length > 0) {
5173
+ messages.push(...pending);
5174
+ emitEvent(onEvent, "messages_updated", { messages });
5175
+ emitEvent(onEvent, "status", { message: `Injected ${pending.length} steering message(s)` });
5176
+ }
5177
+ }
5178
+ if (contextBudget !== void 0) {
5179
+ const [droppedCount] = trimToContextWindow(messages, contextBudget);
5180
+ if (droppedCount > 0) {
5181
+ emitEvent(onEvent, "messages_updated", { messages });
5182
+ emitEvent(onEvent, "status", { message: `Trimmed ${droppedCount} messages for context budget` });
5183
+ }
5184
+ }
5185
+ if (guardrails) {
5186
+ const result = guardrails.checkInput(messages);
5187
+ if (!result.allowed) {
5188
+ emitEvent(onEvent, "error", { message: `Input guardrail denied: ${result.reason}` });
5189
+ throw new GuardrailError(result.reason ?? "Input guardrail denied");
5190
+ }
5191
+ if (result.rewrite) messages = result.rewrite;
5192
+ }
5193
+ try {
5194
+ checkCancellation(signal);
5195
+ } catch (err) {
5196
+ emitEvent(onEvent, "cancelled", {});
5197
+ throw err;
5198
+ }
5199
+ response = await executor.execute(agent, messages);
4975
5200
  if (isAsyncIterable(response)) {
4976
- const { toolCalls, content } = await consumeStream(agent, response);
5201
+ const { toolCalls, content } = await consumeStream(agent, response, onEvent);
5202
+ if (guardrails && content) {
5203
+ const assistantMsg = new Message("assistant", [text(content)]);
5204
+ const gr = guardrails.checkOutput(assistantMsg);
5205
+ if (!gr.allowed) {
5206
+ emitEvent(onEvent, "error", { message: `Output guardrail denied: ${gr.reason}` });
5207
+ throw new GuardrailError(gr.reason ?? "Output guardrail denied");
5208
+ }
5209
+ }
4977
5210
  if (toolCalls.length === 0) {
4978
5211
  emit("iterations", iteration);
4979
5212
  emit("result", content);
5213
+ emitEvent(onEvent, "done", { response: content, messages });
4980
5214
  return content;
4981
5215
  }
4982
5216
  iteration++;
@@ -4986,17 +5220,57 @@ async function invokeAgent(prompt, inputs, options) {
4986
5220
  );
4987
5221
  }
4988
5222
  const toolMessages2 = await traceSpan("toolCalls", async (toolEmit) => {
4989
- toolEmit("signature", "prompty.invokeAgent.toolCalls");
5223
+ toolEmit("signature", "prompty.turn.toolCalls");
4990
5224
  toolEmit("description", `Tool call round ${iteration}`);
4991
- const result2 = await buildToolMessagesFromCalls(toolCalls, content, tools, agent, parentInputs, toolEmit);
4992
- toolEmit("result", result2.map((m) => ({ role: m.role, content: m.parts.map((p) => p.value ?? "").join(""), metadata: m.metadata })));
4993
- return result2;
5225
+ const result = await buildToolMessagesFromCallsWithExtensions(
5226
+ toolCalls,
5227
+ content,
5228
+ tools,
5229
+ agent,
5230
+ parentInputs,
5231
+ toolEmit,
5232
+ { onEvent, signal, guardrails, parallel: parallelToolCalls }
5233
+ );
5234
+ toolEmit("result", result.map((m) => ({ role: m.role, content: m.parts.map((p) => p.value ?? "").join(""), metadata: m.metadata })));
5235
+ return result;
4994
5236
  });
4995
5237
  messages.push(...toolMessages2);
4996
- response = await executor.execute(agent, messages);
5238
+ emitEvent(onEvent, "messages_updated", { messages });
4997
5239
  continue;
4998
5240
  }
4999
- if (!hasToolCalls(response)) break;
5241
+ if (!hasToolCalls(response)) {
5242
+ const finalResult = options?.raw ? response : await process2(agent, response);
5243
+ if (guardrails) {
5244
+ const contentStr = typeof finalResult === "string" ? finalResult : JSON.stringify(finalResult);
5245
+ const assistantMsg = new Message("assistant", [text(contentStr)]);
5246
+ const gr = guardrails.checkOutput(assistantMsg);
5247
+ if (!gr.allowed) {
5248
+ emitEvent(onEvent, "error", { message: `Output guardrail denied: ${gr.reason}` });
5249
+ throw new GuardrailError(gr.reason ?? "Output guardrail denied");
5250
+ }
5251
+ if (gr.rewrite !== void 0) {
5252
+ emit("iterations", iteration);
5253
+ emit("result", gr.rewrite);
5254
+ emitEvent(onEvent, "done", { response: gr.rewrite, messages });
5255
+ return gr.rewrite;
5256
+ }
5257
+ }
5258
+ emit("iterations", iteration);
5259
+ emit("result", finalResult);
5260
+ emitEvent(onEvent, "done", { response: finalResult, messages });
5261
+ return finalResult;
5262
+ }
5263
+ if (guardrails) {
5264
+ const { textContent } = extractToolInfo(response);
5265
+ if (textContent) {
5266
+ const assistantMsg = new Message("assistant", [text(textContent)]);
5267
+ const gr = guardrails.checkOutput(assistantMsg);
5268
+ if (!gr.allowed) {
5269
+ emitEvent(onEvent, "error", { message: `Output guardrail denied: ${gr.reason}` });
5270
+ throw new GuardrailError(gr.reason ?? "Output guardrail denied");
5271
+ }
5272
+ }
5273
+ }
5000
5274
  iteration++;
5001
5275
  if (iteration > maxIterations) {
5002
5276
  throw new Error(
@@ -5004,24 +5278,64 @@ async function invokeAgent(prompt, inputs, options) {
5004
5278
  );
5005
5279
  }
5006
5280
  const toolMessages = await traceSpan("toolCalls", async (toolEmit) => {
5007
- toolEmit("signature", "prompty.invokeAgent.toolCalls");
5281
+ toolEmit("signature", "prompty.turn.toolCalls");
5008
5282
  toolEmit("description", `Tool call round ${iteration}`);
5009
- const result2 = await buildToolResultMessages(response, tools, agent, parentInputs, toolEmit);
5010
- toolEmit("result", result2.map((m) => ({ role: m.role, content: m.parts.map((p) => p.value ?? "").join(""), metadata: m.metadata })));
5011
- return result2;
5283
+ const result = await buildToolResultMessagesWithExtensions(
5284
+ response,
5285
+ tools,
5286
+ agent,
5287
+ parentInputs,
5288
+ toolEmit,
5289
+ { onEvent, signal, guardrails, parallel: parallelToolCalls }
5290
+ );
5291
+ toolEmit("result", result.map((m) => ({ role: m.role, content: m.parts.map((p) => p.value ?? "").join(""), metadata: m.metadata })));
5292
+ return result;
5012
5293
  });
5013
5294
  messages.push(...toolMessages);
5014
- response = await executor.execute(agent, messages);
5295
+ emitEvent(onEvent, "messages_updated", { messages });
5015
5296
  }
5016
- emit("iterations", iteration);
5017
- if (options?.raw) {
5018
- emit("result", response);
5019
- return response;
5020
- }
5021
- const result = await process2(agent, response);
5022
- emit("result", result);
5023
- return result;
5024
5297
  });
5298
+ if (options?.validator) {
5299
+ return cast(rawResult, options.validator);
5300
+ }
5301
+ return rawResult;
5302
+ }
5303
+ function resolveBindings(agent, toolName, args, parentInputs) {
5304
+ if (!parentInputs || !agent.tools || agent.tools.length === 0) return args;
5305
+ const toolDef = agent.tools.find((t) => t.name === toolName);
5306
+ if (!toolDef || !toolDef.bindings || toolDef.bindings.length === 0) return args;
5307
+ const merged = { ...args };
5308
+ for (const binding of toolDef.bindings) {
5309
+ if (binding.input in parentInputs) {
5310
+ merged[binding.name] = parentInputs[binding.input];
5311
+ }
5312
+ }
5313
+ return merged;
5314
+ }
5315
+ function isAsyncIterable(value) {
5316
+ return value != null && typeof value === "object" && Symbol.asyncIterator in value;
5317
+ }
5318
+ function isToolCallLike(item) {
5319
+ return typeof item === "object" && item !== null && "id" in item && "name" in item && "arguments" in item;
5320
+ }
5321
+ async function consumeStream(agent, response, onEvent) {
5322
+ const processed = await process2(agent, response);
5323
+ const toolCalls = [];
5324
+ const textParts = [];
5325
+ if (isAsyncIterable(processed)) {
5326
+ for await (const item of processed) {
5327
+ if (isToolCallLike(item)) {
5328
+ toolCalls.push(item);
5329
+ } else if (typeof item === "string") {
5330
+ textParts.push(item);
5331
+ emitEvent(onEvent, "token", { token: item });
5332
+ }
5333
+ }
5334
+ } else if (typeof processed === "string") {
5335
+ textParts.push(processed);
5336
+ emitEvent(onEvent, "token", { token: processed });
5337
+ }
5338
+ return { toolCalls, content: textParts.join("") };
5025
5339
  }
5026
5340
  function expandThreads(messages, nonces, inputs) {
5027
5341
  if (nonces.size === 0) return messages;
@@ -5090,160 +5404,138 @@ function hasToolCalls(response) {
5090
5404
  }
5091
5405
  return false;
5092
5406
  }
5093
- async function buildToolResultMessages(response, tools, agent, parentInputs, parentEmit) {
5407
+ function extractToolInfo(response) {
5408
+ if (typeof response !== "object" || response === null) {
5409
+ return { toolCalls: [], textContent: "" };
5410
+ }
5094
5411
  const r = response;
5095
5412
  if (Array.isArray(r.content) && r.stop_reason === "tool_use") {
5096
- return buildAnthropicToolResultMessages(r, tools, agent, parentInputs, parentEmit);
5413
+ const content = r.content;
5414
+ const toolCalls = content.filter((b) => b.type === "tool_use").map((b) => ({
5415
+ id: b.id,
5416
+ name: b.name,
5417
+ arguments: JSON.stringify(b.input)
5418
+ }));
5419
+ const textContent = content.filter((b) => b.type === "text").map((b) => b.text).join("");
5420
+ return { toolCalls, textContent };
5097
5421
  }
5098
5422
  if (r.object === "response" && Array.isArray(r.output)) {
5099
- return buildResponsesToolResultMessages(r, tools, agent, parentInputs, parentEmit);
5423
+ const funcCalls = r.output.filter(
5424
+ (item) => item.type === "function_call"
5425
+ );
5426
+ const toolCalls = funcCalls.map((fc) => ({
5427
+ id: fc.call_id ?? fc.id ?? "",
5428
+ call_id: fc.call_id ?? fc.id ?? "",
5429
+ name: fc.name,
5430
+ arguments: fc.arguments ?? "{}"
5431
+ }));
5432
+ return { toolCalls, textContent: "" };
5100
5433
  }
5101
- return buildOpenAIToolResultMessages(r, tools, agent, parentInputs, parentEmit);
5102
- }
5103
- async function buildOpenAIToolResultMessages(r, tools, agent, parentInputs, parentEmit) {
5104
5434
  const choices = r.choices;
5105
- const choice = choices[0];
5106
- const message = choice.message;
5107
- const toolCalls = message.tool_calls;
5108
- const messages = [];
5109
- const assistantContent = message.content ?? "";
5110
- messages.push(
5111
- new Message("assistant", assistantContent ? [text(assistantContent)] : [], {
5112
- tool_calls: toolCalls
5113
- })
5114
- );
5115
- const toolInputs = [];
5116
- for (const tc of toolCalls) {
5117
- const fn = tc.function;
5118
- const toolName = fn.name;
5119
- const toolCallId = tc.id;
5120
- let result;
5121
- let parsedArgs;
5122
- try {
5123
- parsedArgs = JSON.parse(fn.arguments);
5124
- if (agent && parentInputs && typeof parsedArgs === "object" && parsedArgs !== null && !Array.isArray(parsedArgs)) {
5125
- parsedArgs = resolveBindings(agent, toolName, parsedArgs, parentInputs);
5126
- }
5127
- result = await traceSpan(toolName, async (toolEmit) => {
5128
- toolEmit("signature", `prompty.tool.${toolName}`);
5129
- toolEmit("description", `Execute tool: ${toolName}`);
5130
- toolEmit("inputs", { arguments: parsedArgs, tool_call_id: toolCallId });
5131
- const r2 = await dispatchTool(toolName, parsedArgs, tools, agent ?? {}, parentInputs ?? {});
5132
- toolEmit("result", r2);
5133
- return r2;
5435
+ if (Array.isArray(choices) && choices.length > 0) {
5436
+ const choice = choices[0];
5437
+ const message = choice.message;
5438
+ if (message && Array.isArray(message.tool_calls)) {
5439
+ const toolCalls = message.tool_calls.map((tc) => {
5440
+ const fn = tc.function;
5441
+ return {
5442
+ id: tc.id,
5443
+ name: fn.name,
5444
+ arguments: fn.arguments
5445
+ };
5134
5446
  });
5135
- } catch (err) {
5136
- result = `Error: ${err instanceof Error ? err.message : String(err)}`;
5447
+ return { toolCalls, textContent: message.content ?? "" };
5137
5448
  }
5138
- toolInputs.push({ name: toolName, arguments: parsedArgs, tool_call_id: toolCallId, result });
5139
- messages.push(
5140
- new Message("tool", [text(result)], {
5141
- tool_call_id: toolCallId,
5142
- name: toolName
5143
- })
5144
- );
5145
- }
5146
- if (parentEmit) {
5147
- parentEmit("inputs", { tool_calls: toolInputs });
5148
5449
  }
5149
- return messages;
5450
+ return { toolCalls: [], textContent: "" };
5150
5451
  }
5151
- async function buildAnthropicToolResultMessages(r, tools, agent, parentInputs, parentEmit) {
5152
- const content = r.content;
5153
- const toolUseBlocks = content.filter((block) => block.type === "tool_use");
5154
- const messages = [];
5155
- const textParts = content.filter((block) => block.type === "text").map((block) => text(block.text));
5156
- messages.push(
5157
- new Message("assistant", textParts, { content })
5158
- );
5159
- const toolInputs = [];
5160
- const toolResultBlocks = [];
5161
- for (const block of toolUseBlocks) {
5162
- const toolName = block.name;
5163
- const toolCallId = block.id;
5164
- let toolArgs = block.input;
5165
- if (agent && parentInputs && typeof toolArgs === "object" && toolArgs !== null && !Array.isArray(toolArgs)) {
5166
- toolArgs = resolveBindings(agent, toolName, toolArgs, parentInputs);
5167
- }
5168
- let result;
5452
+ async function dispatchOneToolWithExtensions(tc, tools, agent, parentInputs, ext) {
5453
+ const { onEvent, signal, guardrails } = ext;
5454
+ try {
5455
+ checkCancellation(signal);
5456
+ } catch (err) {
5457
+ emitEvent(onEvent, "cancelled", {});
5458
+ throw err;
5459
+ }
5460
+ emitEvent(onEvent, "tool_call_start", { name: tc.name, arguments: tc.arguments });
5461
+ if (guardrails) {
5462
+ let parsedArgs2 = {};
5169
5463
  try {
5170
- result = await traceSpan(toolName, async (toolEmit) => {
5171
- toolEmit("signature", `prompty.tool.${toolName}`);
5172
- toolEmit("description", `Execute tool: ${toolName}`);
5173
- toolEmit("inputs", { arguments: toolArgs, tool_use_id: toolCallId });
5174
- const r2 = await dispatchTool(toolName, toolArgs, tools, agent ?? {}, parentInputs ?? {});
5175
- toolEmit("result", r2);
5176
- return r2;
5177
- });
5178
- } catch (err) {
5179
- result = `Error: ${err instanceof Error ? err.message : String(err)}`;
5464
+ const parsed = JSON.parse(tc.arguments);
5465
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
5466
+ parsedArgs2 = parsed;
5467
+ }
5468
+ } catch {
5469
+ }
5470
+ const gr = guardrails.checkTool(tc.name, parsedArgs2);
5471
+ if (!gr.allowed) {
5472
+ const deniedMsg = `Tool denied by guardrail: ${gr.reason}`;
5473
+ emitEvent(onEvent, "tool_result", { name: tc.name, result: deniedMsg });
5474
+ return deniedMsg;
5475
+ }
5476
+ if (gr.rewrite !== void 0) {
5477
+ tc = { ...tc, arguments: typeof gr.rewrite === "string" ? gr.rewrite : JSON.stringify(gr.rewrite) };
5180
5478
  }
5181
- toolInputs.push({ name: toolName, arguments: toolArgs, tool_use_id: toolCallId, result });
5182
- toolResultBlocks.push({
5183
- type: "tool_result",
5184
- tool_use_id: toolCallId,
5185
- content: result
5186
- });
5187
5479
  }
5188
- if (parentEmit) {
5189
- parentEmit("inputs", { tool_calls: toolInputs });
5480
+ let result;
5481
+ let parsedArgs;
5482
+ try {
5483
+ parsedArgs = JSON.parse(tc.arguments);
5484
+ if (agent && parentInputs && typeof parsedArgs === "object" && parsedArgs !== null && !Array.isArray(parsedArgs)) {
5485
+ parsedArgs = resolveBindings(agent, tc.name, parsedArgs, parentInputs);
5486
+ }
5487
+ result = await traceSpan(tc.name, async (toolEmit) => {
5488
+ toolEmit("signature", `prompty.tool.${tc.name}`);
5489
+ toolEmit("description", `Execute tool: ${tc.name}`);
5490
+ toolEmit("inputs", { arguments: parsedArgs, id: tc.id });
5491
+ const r = await dispatchTool(tc.name, parsedArgs, tools, agent, parentInputs);
5492
+ toolEmit("result", r);
5493
+ return r;
5494
+ });
5495
+ } catch (err) {
5496
+ if (err instanceof CancelledError) throw err;
5497
+ result = `Error: ${err instanceof Error ? err.message : String(err)}`;
5190
5498
  }
5191
- messages.push(
5192
- new Message("user", [], { tool_results: toolResultBlocks })
5193
- );
5194
- return messages;
5499
+ emitEvent(onEvent, "tool_result", { name: tc.name, result });
5500
+ return result;
5195
5501
  }
5196
- async function buildResponsesToolResultMessages(r, tools, agent, parentInputs, parentEmit) {
5197
- const output = r.output;
5198
- const funcCalls = output.filter((item) => item.type === "function_call");
5199
- const messages = [];
5200
- const toolInputs = [];
5201
- for (const fc of funcCalls) {
5202
- const toolName = fc.name;
5203
- const callId = fc.call_id ?? fc.id ?? "";
5204
- const argsStr = fc.arguments ?? "{}";
5205
- messages.push(
5206
- new Message("assistant", [], {
5207
- responses_function_call: {
5208
- type: "function_call",
5209
- call_id: callId,
5210
- name: toolName,
5211
- arguments: argsStr
5212
- }
5213
- })
5214
- );
5215
- let result;
5216
- let parsedArgs;
5217
- try {
5218
- parsedArgs = JSON.parse(argsStr);
5219
- if (agent && parentInputs && typeof parsedArgs === "object" && parsedArgs !== null && !Array.isArray(parsedArgs)) {
5220
- parsedArgs = resolveBindings(agent, toolName, parsedArgs, parentInputs);
5221
- }
5222
- result = await traceSpan(toolName, async (toolEmit) => {
5223
- toolEmit("signature", `prompty.tool.${toolName}`);
5224
- toolEmit("description", `Execute tool: ${toolName}`);
5225
- toolEmit("inputs", { arguments: parsedArgs, call_id: callId });
5226
- const r2 = await dispatchTool(toolName, parsedArgs, tools, agent ?? {}, parentInputs ?? {});
5227
- toolEmit("result", r2);
5228
- return r2;
5229
- });
5230
- } catch (err) {
5231
- result = `Error: ${err instanceof Error ? err.message : String(err)}`;
5232
- }
5233
- toolInputs.push({ name: toolName, arguments: parsedArgs, call_id: callId, result });
5234
- messages.push(
5235
- new Message("tool", [text(result)], {
5236
- tool_call_id: callId,
5237
- name: toolName
5238
- })
5502
+ async function dispatchToolsWithExtensions(toolCalls, tools, agent, parentInputs, ext) {
5503
+ if (ext.parallel && toolCalls.length > 1) {
5504
+ return Promise.all(
5505
+ toolCalls.map((tc) => dispatchOneToolWithExtensions(tc, tools, agent, parentInputs, ext))
5239
5506
  );
5240
5507
  }
5508
+ const results = [];
5509
+ for (const tc of toolCalls) {
5510
+ results.push(await dispatchOneToolWithExtensions(tc, tools, agent, parentInputs, ext));
5511
+ }
5512
+ return results;
5513
+ }
5514
+ async function buildToolResultMessagesWithExtensions(response, tools, agent, parentInputs, parentEmit, ext) {
5515
+ const { toolCalls, textContent } = extractToolInfo(response);
5516
+ const toolResults = await dispatchToolsWithExtensions(toolCalls, tools, agent, parentInputs, ext);
5241
5517
  if (parentEmit) {
5242
- parentEmit("inputs", { tool_calls: toolInputs });
5518
+ parentEmit("inputs", {
5519
+ tool_calls: toolCalls.map((tc, i) => ({ name: tc.name, arguments: tc.arguments, id: tc.id, result: toolResults[i] }))
5520
+ });
5521
+ }
5522
+ const provider = resolveProvider(agent);
5523
+ const executor = getExecutor(provider);
5524
+ return executor.formatToolMessages(response, toolCalls, toolResults, textContent);
5525
+ }
5526
+ async function buildToolMessagesFromCallsWithExtensions(toolCalls, textContent, tools, agent, parentInputs, parentEmit, ext) {
5527
+ const normalizedCalls = toolCalls.map((tc) => ({ id: tc.id, name: tc.name, arguments: tc.arguments }));
5528
+ const toolResults = await dispatchToolsWithExtensions(normalizedCalls, tools, agent, parentInputs, ext);
5529
+ if (parentEmit) {
5530
+ parentEmit("inputs", {
5531
+ tool_calls: normalizedCalls.map((tc, i) => ({ name: tc.name, arguments: tc.arguments, id: tc.id, result: toolResults[i] }))
5532
+ });
5243
5533
  }
5244
- return messages;
5534
+ const provider = resolveProvider(agent);
5535
+ const executor = getExecutor(provider);
5536
+ return executor.formatToolMessages(null, normalizedCalls, toolResults, textContent);
5245
5537
  }
5246
- var DEFAULT_FORMAT, DEFAULT_PARSER, DEFAULT_PROVIDER, DEFAULT_MAX_ITERATIONS;
5538
+ var DEFAULT_FORMAT, DEFAULT_PARSER, DEFAULT_PROVIDER, DEFAULT_MAX_ITERATIONS, invokeAgent;
5247
5539
  var init_pipeline = __esm({
5248
5540
  "src/core/pipeline.ts"() {
5249
5541
  "use strict";
@@ -5253,10 +5545,16 @@ var init_pipeline = __esm({
5253
5545
  init_tracer();
5254
5546
  init_loader();
5255
5547
  init_tool_dispatch();
5548
+ init_agent_events();
5549
+ init_cancellation();
5550
+ init_context2();
5551
+ init_guardrails();
5552
+ init_structured();
5256
5553
  DEFAULT_FORMAT = "nunjucks";
5257
5554
  DEFAULT_PARSER = "prompty";
5258
5555
  DEFAULT_PROVIDER = "openai";
5259
5556
  DEFAULT_MAX_ITERATIONS = 10;
5557
+ invokeAgent = turn;
5260
5558
  }
5261
5559
  });
5262
5560
 
@@ -5286,6 +5584,101 @@ function clearConnections() {
5286
5584
  init_loader();
5287
5585
  init_pipeline();
5288
5586
  init_tool_dispatch();
5587
+ init_agent_events();
5588
+ init_cancellation();
5589
+ init_context2();
5590
+ init_guardrails();
5591
+
5592
+ // src/core/steering.ts
5593
+ init_types();
5594
+ var Steering = class {
5595
+ queue = [];
5596
+ /** Enqueue a message to be injected at the next iteration. */
5597
+ send(message) {
5598
+ this.queue.push(message);
5599
+ }
5600
+ /** Remove and return all queued messages as Message objects. */
5601
+ drain() {
5602
+ const items = this.queue.splice(0);
5603
+ return items.map((text2) => new Message("user", [{ kind: "text", value: text2 }]));
5604
+ }
5605
+ /** Whether there are pending messages without consuming them. */
5606
+ get hasPending() {
5607
+ return this.queue.length > 0;
5608
+ }
5609
+ };
5610
+
5611
+ // src/core/tool-decorator.ts
5612
+ init_tool();
5613
+ init_property();
5614
+ init_tool_dispatch();
5615
+ function tool(fn, options) {
5616
+ const toolName = options?.name ?? fn.name;
5617
+ const toolDesc = options?.description ?? "";
5618
+ const shouldRegister = options?.register !== false;
5619
+ const properties = (options?.parameters ?? []).map(
5620
+ (p) => new Property({
5621
+ name: p.name,
5622
+ kind: p.kind ?? "string",
5623
+ required: p.required ?? p.default === void 0,
5624
+ description: p.description,
5625
+ default: p.default
5626
+ })
5627
+ );
5628
+ const toolDef = new FunctionTool({
5629
+ name: toolName,
5630
+ kind: "function",
5631
+ description: toolDesc,
5632
+ parameters: properties
5633
+ });
5634
+ const wrapped = fn;
5635
+ wrapped.__tool__ = toolDef;
5636
+ if (shouldRegister) {
5637
+ registerTool(toolName, fn);
5638
+ }
5639
+ return wrapped;
5640
+ }
5641
+ function bindTools(agent, tools) {
5642
+ const handlers = {};
5643
+ for (const fn of tools) {
5644
+ const toolDef = fn.__tool__;
5645
+ if (!toolDef) {
5646
+ throw new Error(
5647
+ `Function '${fn.name || "(anonymous)"}' is not a tool()-wrapped function (missing __tool__ property)`
5648
+ );
5649
+ }
5650
+ const name = toolDef.name;
5651
+ if (name in handlers) {
5652
+ throw new Error(`Duplicate tool handler: '${name}'`);
5653
+ }
5654
+ handlers[name] = fn;
5655
+ }
5656
+ const declaredFunctionTools = /* @__PURE__ */ new Set();
5657
+ for (const toolDef of agent.tools ?? []) {
5658
+ if (toolDef.kind === "function") {
5659
+ declaredFunctionTools.add(toolDef.name);
5660
+ }
5661
+ }
5662
+ for (const name of Object.keys(handlers)) {
5663
+ if (!declaredFunctionTools.has(name)) {
5664
+ const declared = [...declaredFunctionTools].sort().join(", ") || "(none)";
5665
+ throw new Error(
5666
+ `Tool handler '${name}' has no matching 'kind: function' declaration in agent.tools. Declared function tools: ${declared}`
5667
+ );
5668
+ }
5669
+ }
5670
+ for (const name of declaredFunctionTools) {
5671
+ if (!(name in handlers)) {
5672
+ console.warn(
5673
+ `Tool '${name}' is declared in agent.tools but no handler was provided to bindTools()`
5674
+ );
5675
+ }
5676
+ }
5677
+ return handlers;
5678
+ }
5679
+
5680
+ // src/core/index.ts
5681
+ init_structured();
5289
5682
 
5290
5683
  // src/renderers/nunjucks.ts
5291
5684
  init_common();
@@ -5678,11 +6071,14 @@ export {
5678
6071
  ApiKeyConnection,
5679
6072
  ArrayProperty,
5680
6073
  Binding,
6074
+ CancelledError,
5681
6075
  Connection,
5682
6076
  CustomTool,
5683
6077
  FormatConfig,
5684
6078
  FoundryConnection,
5685
6079
  FunctionTool,
6080
+ GuardrailError,
6081
+ Guardrails,
5686
6082
  InvokerError,
5687
6083
  LoadContext,
5688
6084
  McpApprovalMode,
@@ -5708,15 +6104,23 @@ export {
5708
6104
  ReferenceConnection,
5709
6105
  RemoteConnection,
5710
6106
  SaveContext,
6107
+ Steering,
6108
+ StructuredResultSymbol,
5711
6109
  Template,
5712
6110
  ThreadMarker,
5713
6111
  Tool,
5714
6112
  Tracer,
6113
+ bindTools,
6114
+ cast,
6115
+ checkCancellation,
5715
6116
  clearCache,
5716
6117
  clearConnections,
5717
6118
  consoleTracer,
6119
+ createStructuredResult,
5718
6120
  dictContentToPart,
5719
6121
  dictToMessage,
6122
+ emitEvent,
6123
+ estimateChars,
5720
6124
  getConnection,
5721
6125
  getExecutor,
5722
6126
  getParser,
@@ -5724,6 +6128,7 @@ export {
5724
6128
  getRenderer,
5725
6129
  invoke,
5726
6130
  invokeAgent,
6131
+ isStructuredResult,
5727
6132
  load,
5728
6133
  otelTracer,
5729
6134
  parse,
@@ -5738,12 +6143,16 @@ export {
5738
6143
  resolveBindings,
5739
6144
  run,
5740
6145
  sanitizeValue,
6146
+ summarizeDropped,
5741
6147
  text,
5742
6148
  textMessage,
5743
6149
  toSerializable,
6150
+ tool,
5744
6151
  trace,
5745
6152
  traceMethod,
5746
6153
  traceSpan,
6154
+ trimToContextWindow,
6155
+ turn,
5747
6156
  validateInputs
5748
6157
  };
5749
6158
  //# sourceMappingURL=index.js.map