@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.cjs CHANGED
@@ -2236,7 +2236,12 @@ var init_property = __esm({
2236
2236
  for (const [key, value] of Object.entries(
2237
2237
  data
2238
2238
  )) {
2239
- if (value && typeof value === "object" && !Array.isArray(value)) {
2239
+ if (Array.isArray(value)) {
2240
+ throw new Error(
2241
+ `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.`
2242
+ );
2243
+ }
2244
+ if (value && typeof value === "object") {
2240
2245
  value["name"] = key;
2241
2246
  result.push(Property.load(value, context));
2242
2247
  } else {
@@ -3144,7 +3149,12 @@ var init_tool = __esm({
3144
3149
  for (const [key, value] of Object.entries(
3145
3150
  data
3146
3151
  )) {
3147
- if (value && typeof value === "object" && !Array.isArray(value)) {
3152
+ if (Array.isArray(value)) {
3153
+ throw new Error(
3154
+ `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.`
3155
+ );
3156
+ }
3157
+ if (value && typeof value === "object") {
3148
3158
  value["name"] = key;
3149
3159
  result.push(Binding.load(value, context));
3150
3160
  } else {
@@ -3359,7 +3369,12 @@ var init_tool = __esm({
3359
3369
  for (const [key, value] of Object.entries(
3360
3370
  data
3361
3371
  )) {
3362
- if (value && typeof value === "object" && !Array.isArray(value)) {
3372
+ if (Array.isArray(value)) {
3373
+ throw new Error(
3374
+ `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.`
3375
+ );
3376
+ }
3377
+ if (value && typeof value === "object") {
3363
3378
  value["name"] = key;
3364
3379
  result.push(Property.load(value, context));
3365
3380
  } else {
@@ -4116,7 +4131,12 @@ var init_prompty = __esm({
4116
4131
  for (const [key, value] of Object.entries(
4117
4132
  data
4118
4133
  )) {
4119
- if (value && typeof value === "object" && !Array.isArray(value)) {
4134
+ if (Array.isArray(value)) {
4135
+ throw new Error(
4136
+ `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.`
4137
+ );
4138
+ }
4139
+ if (value && typeof value === "object") {
4120
4140
  value["name"] = key;
4121
4141
  result.push(Property.load(value, context));
4122
4142
  } else {
@@ -4146,7 +4166,12 @@ var init_prompty = __esm({
4146
4166
  for (const [key, value] of Object.entries(
4147
4167
  data
4148
4168
  )) {
4149
- if (value && typeof value === "object" && !Array.isArray(value)) {
4169
+ if (Array.isArray(value)) {
4170
+ throw new Error(
4171
+ `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.`
4172
+ );
4173
+ }
4174
+ if (value && typeof value === "object") {
4150
4175
  value["name"] = key;
4151
4176
  result.push(Property.load(value, context));
4152
4177
  } else {
@@ -4176,7 +4201,12 @@ var init_prompty = __esm({
4176
4201
  for (const [key, value] of Object.entries(
4177
4202
  data
4178
4203
  )) {
4179
- if (value && typeof value === "object" && !Array.isArray(value)) {
4204
+ if (Array.isArray(value)) {
4205
+ throw new Error(
4206
+ `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.`
4207
+ );
4208
+ }
4209
+ if (value && typeof value === "object") {
4180
4210
  value["name"] = key;
4181
4211
  result.push(Tool.load(value, context));
4182
4212
  } else {
@@ -4515,6 +4545,9 @@ var init_common = __esm({
4515
4545
  });
4516
4546
 
4517
4547
  // src/core/tool-dispatch.ts
4548
+ function registerTool(name, handler) {
4549
+ nameRegistry.set(name, handler);
4550
+ }
4518
4551
  function getTool(name) {
4519
4552
  return nameRegistry.get(name);
4520
4553
  }
@@ -4538,12 +4571,12 @@ async function dispatchTool(toolName, args, userTools, agent, parentInputs) {
4538
4571
  const result = await registeredFn(args);
4539
4572
  return typeof result === "string" ? result : JSON.stringify(result);
4540
4573
  }
4541
- const tool = agent.tools?.find((t) => t.name === toolName);
4542
- if (!tool) {
4574
+ const tool2 = agent.tools?.find((t) => t.name === toolName);
4575
+ if (!tool2) {
4543
4576
  const available = Object.keys(userTools).sort().join(", ") || "(none)";
4544
4577
  return `Error: tool "${toolName}" not found in userTools or agent.tools. Available user tools: ${available}`;
4545
4578
  }
4546
- const kind = tool.kind || "*";
4579
+ const kind = tool2.kind || "*";
4547
4580
  let handler;
4548
4581
  try {
4549
4582
  handler = getToolHandler(kind);
@@ -4555,7 +4588,7 @@ async function dispatchTool(toolName, args, userTools, agent, parentInputs) {
4555
4588
  }
4556
4589
  }
4557
4590
  return await handler.executeTool(
4558
- tool,
4591
+ tool2,
4559
4592
  args,
4560
4593
  agent,
4561
4594
  parentInputs
@@ -4581,36 +4614,36 @@ var init_tool_dispatch = __esm({
4581
4614
  nameRegistry = /* @__PURE__ */ new Map();
4582
4615
  toolHandlers = /* @__PURE__ */ new Map();
4583
4616
  FunctionToolHandler = class {
4584
- async executeTool(tool, _args, _agent, _parentInputs) {
4585
- const name = tool.name ?? "unknown";
4617
+ async executeTool(tool2, _args, _agent, _parentInputs) {
4618
+ const name = tool2.name ?? "unknown";
4586
4619
  throw new Error(
4587
- `Function tool '${name}' declared but no callable provided. Pass it via tools: { '${name}': fn } in invokeAgent().`
4620
+ `Function tool '${name}' declared but no callable provided. Pass it via tools: { '${name}': fn } in turn().`
4588
4621
  );
4589
4622
  }
4590
4623
  };
4591
4624
  PromptyToolHandler = class {
4592
- async executeTool(tool, args, agent, _parentInputs) {
4625
+ async executeTool(tool2, args, agent, _parentInputs) {
4593
4626
  const { load: load2 } = await Promise.resolve().then(() => (init_loader(), loader_exports));
4594
- const { prepare: prepare2, run: run2, invokeAgent: invokeAgent2 } = await Promise.resolve().then(() => (init_pipeline(), pipeline_exports));
4627
+ const { prepare: prepare2, run: run2, turn: turn2 } = await Promise.resolve().then(() => (init_pipeline(), pipeline_exports));
4595
4628
  const parentPath = (agent.metadata ?? {}).__source_path;
4596
4629
  if (!parentPath) {
4597
- return `Error: cannot resolve PromptyTool '${tool.name}': parent has no __source_path`;
4630
+ return `Error: cannot resolve PromptyTool '${tool2.name}': parent has no __source_path`;
4598
4631
  }
4599
- const childPath = (0, import_node_path2.resolve)((0, import_node_path2.dirname)(parentPath), tool.path);
4632
+ const childPath = (0, import_node_path2.resolve)((0, import_node_path2.dirname)(parentPath), tool2.path);
4600
4633
  const stack = (agent.metadata ?? {}).__prompty_tool_stack ?? [];
4601
4634
  const normalizedChild = (0, import_node_path2.resolve)(childPath);
4602
4635
  const visited = /* @__PURE__ */ new Set([...stack.map((p) => (0, import_node_path2.resolve)(p)), (0, import_node_path2.resolve)(parentPath)]);
4603
4636
  if (visited.has(normalizedChild)) {
4604
4637
  const chain = [...stack, parentPath, childPath].join(" \u2192 ");
4605
- return `Error executing PromptyTool '${tool.name}': circular reference detected: ${chain}`;
4638
+ return `Error executing PromptyTool '${tool2.name}': circular reference detected: ${chain}`;
4606
4639
  }
4607
4640
  try {
4608
4641
  const child = load2(childPath);
4609
4642
  if (!child.metadata) child.metadata = {};
4610
4643
  child.metadata.__prompty_tool_stack = [...stack, parentPath];
4611
- const mode = tool.mode ?? "single";
4644
+ const mode = tool2.mode ?? "single";
4612
4645
  if (mode === "agentic") {
4613
- const result = await invokeAgent2(child, args);
4646
+ const result = await turn2(child, args);
4614
4647
  return typeof result === "string" ? result : JSON.stringify(result);
4615
4648
  } else {
4616
4649
  const messages = await prepare2(child, args);
@@ -4618,7 +4651,7 @@ var init_tool_dispatch = __esm({
4618
4651
  return typeof result === "string" ? result : JSON.stringify(result);
4619
4652
  }
4620
4653
  } catch (err) {
4621
- return `Error executing PromptyTool '${tool.name}': ${err instanceof Error ? err.message : String(err)}`;
4654
+ return `Error executing PromptyTool '${tool2.name}': ${err instanceof Error ? err.message : String(err)}`;
4622
4655
  }
4623
4656
  }
4624
4657
  };
@@ -4645,6 +4678,205 @@ var init_tool_dispatch = __esm({
4645
4678
  }
4646
4679
  });
4647
4680
 
4681
+ // src/core/agent-events.ts
4682
+ function emitEvent(callback, eventType, data) {
4683
+ if (!callback) return;
4684
+ try {
4685
+ callback(eventType, data);
4686
+ } catch (err) {
4687
+ if (typeof globalThis.console?.debug === "function") {
4688
+ globalThis.console.debug(`Event callback error for ${eventType}:`, err);
4689
+ }
4690
+ }
4691
+ }
4692
+ var init_agent_events = __esm({
4693
+ "src/core/agent-events.ts"() {
4694
+ "use strict";
4695
+ }
4696
+ });
4697
+
4698
+ // src/core/cancellation.ts
4699
+ function checkCancellation(signal) {
4700
+ if (signal?.aborted) {
4701
+ throw new CancelledError();
4702
+ }
4703
+ }
4704
+ var CancelledError;
4705
+ var init_cancellation = __esm({
4706
+ "src/core/cancellation.ts"() {
4707
+ "use strict";
4708
+ CancelledError = class extends Error {
4709
+ constructor(message = "Agent loop cancelled") {
4710
+ super(message);
4711
+ this.name = "CancelledError";
4712
+ }
4713
+ };
4714
+ }
4715
+ });
4716
+
4717
+ // src/core/context.ts
4718
+ function estimateChars(messages) {
4719
+ let total = 0;
4720
+ for (const msg of messages) {
4721
+ total += msg.role.length + 4;
4722
+ for (const part of msg.parts) {
4723
+ if (part.kind === "text") {
4724
+ total += part.value.length;
4725
+ } else {
4726
+ total += 200;
4727
+ }
4728
+ }
4729
+ const toolCalls = msg.metadata?.tool_calls;
4730
+ if (toolCalls) {
4731
+ total += JSON.stringify(toolCalls).length;
4732
+ }
4733
+ }
4734
+ return total;
4735
+ }
4736
+ function truncate(text2, maxLen = 200) {
4737
+ return text2.length <= maxLen ? text2 : text2.slice(0, maxLen) + "\u2026";
4738
+ }
4739
+ function summarizeDropped(messages) {
4740
+ const lines = [];
4741
+ for (const msg of messages) {
4742
+ const msgText = msg.text.trim();
4743
+ if (msg.role === "user" && msgText) {
4744
+ lines.push(`User asked: ${truncate(msgText)}`);
4745
+ } else if (msg.role === "assistant") {
4746
+ if (msgText) lines.push(`Assistant: ${truncate(msgText)}`);
4747
+ const toolCalls = msg.metadata?.tool_calls;
4748
+ if (Array.isArray(toolCalls)) {
4749
+ const names = toolCalls.map(
4750
+ (tc) => tc.name ?? (tc.function?.name ?? "?")
4751
+ );
4752
+ lines.push(` Called tools: ${names.join(", ")}`);
4753
+ }
4754
+ }
4755
+ }
4756
+ if (lines.length === 0) return "";
4757
+ let result = "[Context summary: ";
4758
+ for (const line of lines) {
4759
+ if (result.length + line.length > 4e3) {
4760
+ result += "\n... (older messages omitted)";
4761
+ break;
4762
+ }
4763
+ result += line + "\n";
4764
+ }
4765
+ return result.trimEnd() + "]";
4766
+ }
4767
+ function trimToContextWindow(messages, budgetChars) {
4768
+ if (estimateChars(messages) <= budgetChars) {
4769
+ return [0, []];
4770
+ }
4771
+ let systemEnd = 0;
4772
+ for (let i = 0; i < messages.length; i++) {
4773
+ if (messages[i].role !== "system") {
4774
+ systemEnd = i;
4775
+ break;
4776
+ }
4777
+ if (i === messages.length - 1) systemEnd = messages.length;
4778
+ }
4779
+ const systemMsgs = messages.slice(0, systemEnd);
4780
+ const rest = messages.slice(systemEnd);
4781
+ const summaryBudget = Math.min(5e3, Math.floor(budgetChars * 0.05));
4782
+ const dropped = [];
4783
+ while (estimateChars([...systemMsgs, ...rest]) > budgetChars - summaryBudget && rest.length > 2) {
4784
+ dropped.push(rest.shift());
4785
+ }
4786
+ const droppedCount = dropped.length;
4787
+ messages.length = 0;
4788
+ messages.push(...systemMsgs);
4789
+ if (droppedCount > 0) {
4790
+ const summaryText = summarizeDropped(dropped);
4791
+ if (summaryText) {
4792
+ messages.push(new Message("user", [{ kind: "text", value: summaryText }]));
4793
+ }
4794
+ }
4795
+ messages.push(...rest);
4796
+ return [droppedCount, dropped];
4797
+ }
4798
+ var init_context2 = __esm({
4799
+ "src/core/context.ts"() {
4800
+ "use strict";
4801
+ init_types();
4802
+ }
4803
+ });
4804
+
4805
+ // src/core/guardrails.ts
4806
+ var GuardrailError, Guardrails;
4807
+ var init_guardrails = __esm({
4808
+ "src/core/guardrails.ts"() {
4809
+ "use strict";
4810
+ GuardrailError = class extends Error {
4811
+ reason;
4812
+ constructor(reason) {
4813
+ super(`Guardrail denied: ${reason}`);
4814
+ this.name = "GuardrailError";
4815
+ this.reason = reason;
4816
+ }
4817
+ };
4818
+ Guardrails = class {
4819
+ inputHook;
4820
+ outputHook;
4821
+ toolHook;
4822
+ constructor(options) {
4823
+ this.inputHook = options?.input;
4824
+ this.outputHook = options?.output;
4825
+ this.toolHook = options?.tool;
4826
+ }
4827
+ checkInput(messages) {
4828
+ if (!this.inputHook) return { allowed: true };
4829
+ return this.inputHook(messages);
4830
+ }
4831
+ checkOutput(message) {
4832
+ if (!this.outputHook) return { allowed: true };
4833
+ return this.outputHook(message);
4834
+ }
4835
+ checkTool(name, args) {
4836
+ if (!this.toolHook) return { allowed: true };
4837
+ return this.toolHook(name, args);
4838
+ }
4839
+ };
4840
+ }
4841
+ });
4842
+
4843
+ // src/core/structured.ts
4844
+ function createStructuredResult(data, rawJson) {
4845
+ const result = { ...data };
4846
+ Object.defineProperty(result, StructuredResultSymbol, {
4847
+ value: rawJson,
4848
+ writable: false,
4849
+ enumerable: false,
4850
+ configurable: false
4851
+ });
4852
+ return result;
4853
+ }
4854
+ function isStructuredResult(value) {
4855
+ return typeof value === "object" && value !== null && StructuredResultSymbol in value;
4856
+ }
4857
+ function cast(result, validator) {
4858
+ let jsonStr;
4859
+ if (isStructuredResult(result)) {
4860
+ jsonStr = result[StructuredResultSymbol];
4861
+ } else if (typeof result === "string") {
4862
+ jsonStr = result;
4863
+ } else {
4864
+ jsonStr = JSON.stringify(result);
4865
+ }
4866
+ const parsed = JSON.parse(jsonStr);
4867
+ if (validator) {
4868
+ return validator(parsed);
4869
+ }
4870
+ return parsed;
4871
+ }
4872
+ var StructuredResultSymbol;
4873
+ var init_structured = __esm({
4874
+ "src/core/structured.ts"() {
4875
+ "use strict";
4876
+ StructuredResultSymbol = /* @__PURE__ */ Symbol("prompty.rawJson");
4877
+ }
4878
+ });
4879
+
4648
4880
  // src/core/pipeline.ts
4649
4881
  var pipeline_exports = {};
4650
4882
  __export(pipeline_exports, {
@@ -4656,6 +4888,7 @@ __export(pipeline_exports, {
4656
4888
  render: () => render,
4657
4889
  resolveBindings: () => resolveBindings,
4658
4890
  run: () => run,
4891
+ turn: () => turn,
4659
4892
  validateInputs: () => validateInputs
4660
4893
  });
4661
4894
  function sanitizeNonces(value) {
@@ -4846,129 +5079,27 @@ async function invoke(prompt, inputs, options) {
4846
5079
  emit("description", "Invoke a prompty");
4847
5080
  emit("inputs", { prompt: serializeAgent(agent), inputs: inputs ?? {} });
4848
5081
  const messages = await prepare(agent, inputs);
4849
- const result = await run(agent, messages, options);
4850
- emit("result", result);
4851
- return result;
4852
- });
4853
- }
4854
- function resolveBindings(agent, toolName, args, parentInputs) {
4855
- if (!parentInputs || !agent.tools || agent.tools.length === 0) return args;
4856
- const toolDef = agent.tools.find((t) => t.name === toolName);
4857
- if (!toolDef || !toolDef.bindings || toolDef.bindings.length === 0) return args;
4858
- const merged = { ...args };
4859
- for (const binding of toolDef.bindings) {
4860
- if (binding.input in parentInputs) {
4861
- merged[binding.name] = parentInputs[binding.input];
4862
- }
4863
- }
4864
- return merged;
4865
- }
4866
- function isAsyncIterable(value) {
4867
- return value != null && typeof value === "object" && Symbol.asyncIterator in value;
4868
- }
4869
- function isToolCallLike(item) {
4870
- return typeof item === "object" && item !== null && "id" in item && "name" in item && "arguments" in item;
4871
- }
4872
- async function consumeStream(agent, response) {
4873
- const processed = await process2(agent, response);
4874
- const toolCalls = [];
4875
- const textParts = [];
4876
- if (isAsyncIterable(processed)) {
4877
- for await (const item of processed) {
4878
- if (isToolCallLike(item)) {
4879
- toolCalls.push(item);
4880
- } else if (typeof item === "string") {
4881
- textParts.push(item);
5082
+ const provider = resolveProvider(agent);
5083
+ const executor = getExecutor(provider);
5084
+ const response = await executor.execute(agent, messages);
5085
+ if (options?.raw) {
5086
+ emit("result", response);
5087
+ if (options?.validator) {
5088
+ return cast(response, options.validator);
4882
5089
  }
5090
+ return response;
4883
5091
  }
4884
- } else if (typeof processed === "string") {
4885
- textParts.push(processed);
4886
- }
4887
- return { toolCalls, content: textParts.join("") };
4888
- }
4889
- async function buildToolMessagesFromCalls(toolCalls, textContent, tools, agent, parentInputs, parentEmit) {
4890
- const provider = resolveProvider(agent);
4891
- const apiType = agent.model?.apiType || "chat";
4892
- const messages = [];
4893
- const toolInputs = [];
4894
- if (provider === "anthropic") {
4895
- const rawContent = [];
4896
- if (textContent) rawContent.push({ type: "text", text: textContent });
4897
- for (const tc of toolCalls) {
4898
- rawContent.push({
4899
- type: "tool_use",
4900
- id: tc.id,
4901
- name: tc.name,
4902
- input: JSON.parse(tc.arguments)
4903
- });
4904
- }
4905
- messages.push(
4906
- new Message("assistant", textContent ? [text(textContent)] : [], { content: rawContent })
4907
- );
4908
- } else if (apiType === "responses") {
4909
- for (const tc of toolCalls) {
4910
- messages.push(
4911
- new Message("assistant", [], {
4912
- responses_function_call: {
4913
- type: "function_call",
4914
- call_id: tc.id,
4915
- name: tc.name,
4916
- arguments: tc.arguments
4917
- }
4918
- })
4919
- );
4920
- }
4921
- } else {
4922
- const rawToolCalls = toolCalls.map((tc) => ({
4923
- id: tc.id,
4924
- type: "function",
4925
- function: { name: tc.name, arguments: tc.arguments }
4926
- }));
4927
- messages.push(
4928
- new Message("assistant", textContent ? [text(textContent)] : [], {
4929
- tool_calls: rawToolCalls
4930
- })
4931
- );
4932
- }
4933
- const toolResultBlocks = [];
4934
- for (const tc of toolCalls) {
4935
- let result;
4936
- let parsedArgs;
4937
- try {
4938
- parsedArgs = JSON.parse(tc.arguments);
4939
- if (parentInputs && typeof parsedArgs === "object" && parsedArgs !== null && !Array.isArray(parsedArgs)) {
4940
- parsedArgs = resolveBindings(agent, tc.name, parsedArgs, parentInputs);
4941
- }
4942
- result = await traceSpan(tc.name, async (toolEmit) => {
4943
- toolEmit("signature", `prompty.tool.${tc.name}`);
4944
- toolEmit("description", `Execute tool: ${tc.name}`);
4945
- toolEmit("inputs", { arguments: parsedArgs, id: tc.id });
4946
- const r = await dispatchTool(tc.name, parsedArgs, tools, agent, parentInputs ?? {});
4947
- toolEmit("result", r);
4948
- return r;
4949
- });
4950
- } catch (err) {
4951
- result = `Error: ${err instanceof Error ? err.message : String(err)}`;
4952
- }
4953
- toolInputs.push({ name: tc.name, arguments: parsedArgs, id: tc.id, result });
4954
- if (provider === "anthropic") {
4955
- toolResultBlocks.push({ type: "tool_result", tool_use_id: tc.id, content: result });
4956
- } else {
4957
- messages.push(
4958
- new Message("tool", [text(result)], { tool_call_id: tc.id, name: tc.name })
4959
- );
5092
+ const result = await process2(agent, response);
5093
+ emit("result", result);
5094
+ if (options?.validator) {
5095
+ return cast(result, options.validator);
4960
5096
  }
4961
- }
4962
- if (provider === "anthropic" && toolResultBlocks.length > 0) {
4963
- messages.push(new Message("user", [], { tool_results: toolResultBlocks }));
4964
- }
4965
- if (parentEmit) {
4966
- parentEmit("inputs", { tool_calls: toolInputs });
4967
- }
4968
- return messages;
5097
+ return result;
5098
+ });
4969
5099
  }
4970
- async function invokeAgent(prompt, inputs, options) {
4971
- return traceSpan("invokeAgent", async (emit) => {
5100
+ async function turn(prompt, inputs, options) {
5101
+ const label = options?.turn != null ? `turn ${options.turn}` : "turn";
5102
+ const rawResult = await traceSpan(label, async (emit) => {
4972
5103
  const agent = typeof prompt === "string" ? await traceSpan("load", async (loadEmit) => {
4973
5104
  loadEmit("signature", "prompty.load");
4974
5105
  loadEmit("description", "Load a prompty file.");
@@ -4977,23 +5108,126 @@ async function invokeAgent(prompt, inputs, options) {
4977
5108
  loadEmit("result", serializeAgent(loaded));
4978
5109
  return loaded;
4979
5110
  }) : prompt;
5111
+ emit("signature", "prompty.turn");
5112
+ emit("description", label);
5113
+ emit("inputs", sanitizeValue("inputs", inputs));
4980
5114
  const tools = options?.tools ?? {};
5115
+ const hasTools = Object.keys(tools).length > 0;
5116
+ if (!hasTools) {
5117
+ let messages2 = await prepare(agent, inputs);
5118
+ const onEvent2 = options?.onEvent;
5119
+ if (options?.steering) {
5120
+ const pending = options.steering.drain();
5121
+ if (pending.length > 0) {
5122
+ messages2.push(...pending);
5123
+ emitEvent(onEvent2, "messages_updated", { messages: messages2 });
5124
+ }
5125
+ }
5126
+ if (options?.contextBudget !== void 0) {
5127
+ const [droppedCount] = trimToContextWindow(messages2, options.contextBudget);
5128
+ if (droppedCount > 0) {
5129
+ emitEvent(onEvent2, "messages_updated", { messages: messages2 });
5130
+ }
5131
+ }
5132
+ if (options?.guardrails) {
5133
+ const result = options.guardrails.checkInput(messages2);
5134
+ if (!result.allowed) {
5135
+ emitEvent(onEvent2, "error", { message: `Input guardrail denied: ${result.reason}` });
5136
+ throw new GuardrailError(result.reason ?? "Input guardrail denied");
5137
+ }
5138
+ if (result.rewrite) messages2 = result.rewrite;
5139
+ }
5140
+ checkCancellation(options?.signal);
5141
+ const provider2 = resolveProvider(agent);
5142
+ const executor2 = getExecutor(provider2);
5143
+ const response2 = await executor2.execute(agent, messages2);
5144
+ if (options?.raw) {
5145
+ emit("result", response2);
5146
+ return response2;
5147
+ }
5148
+ const processed = await process2(agent, response2);
5149
+ if (options?.guardrails) {
5150
+ const contentStr = typeof processed === "string" ? processed : JSON.stringify(processed);
5151
+ const assistantMsg = new Message("assistant", [text(contentStr)]);
5152
+ const gr = options.guardrails.checkOutput(assistantMsg);
5153
+ if (!gr.allowed) {
5154
+ emitEvent(onEvent2, "error", { message: `Output guardrail denied: ${gr.reason}` });
5155
+ throw new GuardrailError(gr.reason ?? "Output guardrail denied");
5156
+ }
5157
+ if (gr.rewrite !== void 0) {
5158
+ emit("result", gr.rewrite);
5159
+ emitEvent(onEvent2, "done", { response: gr.rewrite, messages: messages2 });
5160
+ return gr.rewrite;
5161
+ }
5162
+ }
5163
+ emit("result", sanitizeValue("result", processed));
5164
+ emitEvent(onEvent2, "done", { response: processed, messages: messages2 });
5165
+ return processed;
5166
+ }
4981
5167
  const maxIterations = options?.maxIterations ?? DEFAULT_MAX_ITERATIONS;
4982
- emit("signature", "prompty.invokeAgent");
4983
- emit("description", "Invoke a prompty with tool calling");
4984
- emit("inputs", { prompt: serializeAgent(agent), tools: Object.keys(tools), inputs: inputs ?? {} });
4985
- const messages = await prepare(agent, inputs);
5168
+ const onEvent = options?.onEvent;
5169
+ const signal = options?.signal;
5170
+ const contextBudget = options?.contextBudget;
5171
+ const guardrails = options?.guardrails;
5172
+ const steering = options?.steering;
5173
+ const parallelToolCalls = options?.parallelToolCalls ?? false;
5174
+ let messages = await prepare(agent, inputs);
4986
5175
  const parentInputs = inputs ?? {};
4987
5176
  const provider = resolveProvider(agent);
4988
5177
  const executor = getExecutor(provider);
4989
- let response = await executor.execute(agent, messages);
5178
+ let response = null;
4990
5179
  let iteration = 0;
4991
5180
  while (true) {
5181
+ try {
5182
+ checkCancellation(signal);
5183
+ } catch (err) {
5184
+ emitEvent(onEvent, "cancelled", {});
5185
+ throw err;
5186
+ }
5187
+ if (steering) {
5188
+ const pending = steering.drain();
5189
+ if (pending.length > 0) {
5190
+ messages.push(...pending);
5191
+ emitEvent(onEvent, "messages_updated", { messages });
5192
+ emitEvent(onEvent, "status", { message: `Injected ${pending.length} steering message(s)` });
5193
+ }
5194
+ }
5195
+ if (contextBudget !== void 0) {
5196
+ const [droppedCount] = trimToContextWindow(messages, contextBudget);
5197
+ if (droppedCount > 0) {
5198
+ emitEvent(onEvent, "messages_updated", { messages });
5199
+ emitEvent(onEvent, "status", { message: `Trimmed ${droppedCount} messages for context budget` });
5200
+ }
5201
+ }
5202
+ if (guardrails) {
5203
+ const result = guardrails.checkInput(messages);
5204
+ if (!result.allowed) {
5205
+ emitEvent(onEvent, "error", { message: `Input guardrail denied: ${result.reason}` });
5206
+ throw new GuardrailError(result.reason ?? "Input guardrail denied");
5207
+ }
5208
+ if (result.rewrite) messages = result.rewrite;
5209
+ }
5210
+ try {
5211
+ checkCancellation(signal);
5212
+ } catch (err) {
5213
+ emitEvent(onEvent, "cancelled", {});
5214
+ throw err;
5215
+ }
5216
+ response = await executor.execute(agent, messages);
4992
5217
  if (isAsyncIterable(response)) {
4993
- const { toolCalls, content } = await consumeStream(agent, response);
5218
+ const { toolCalls, content } = await consumeStream(agent, response, onEvent);
5219
+ if (guardrails && content) {
5220
+ const assistantMsg = new Message("assistant", [text(content)]);
5221
+ const gr = guardrails.checkOutput(assistantMsg);
5222
+ if (!gr.allowed) {
5223
+ emitEvent(onEvent, "error", { message: `Output guardrail denied: ${gr.reason}` });
5224
+ throw new GuardrailError(gr.reason ?? "Output guardrail denied");
5225
+ }
5226
+ }
4994
5227
  if (toolCalls.length === 0) {
4995
5228
  emit("iterations", iteration);
4996
5229
  emit("result", content);
5230
+ emitEvent(onEvent, "done", { response: content, messages });
4997
5231
  return content;
4998
5232
  }
4999
5233
  iteration++;
@@ -5003,17 +5237,57 @@ async function invokeAgent(prompt, inputs, options) {
5003
5237
  );
5004
5238
  }
5005
5239
  const toolMessages2 = await traceSpan("toolCalls", async (toolEmit) => {
5006
- toolEmit("signature", "prompty.invokeAgent.toolCalls");
5240
+ toolEmit("signature", "prompty.turn.toolCalls");
5007
5241
  toolEmit("description", `Tool call round ${iteration}`);
5008
- const result2 = await buildToolMessagesFromCalls(toolCalls, content, tools, agent, parentInputs, toolEmit);
5009
- toolEmit("result", result2.map((m) => ({ role: m.role, content: m.parts.map((p) => p.value ?? "").join(""), metadata: m.metadata })));
5010
- return result2;
5242
+ const result = await buildToolMessagesFromCallsWithExtensions(
5243
+ toolCalls,
5244
+ content,
5245
+ tools,
5246
+ agent,
5247
+ parentInputs,
5248
+ toolEmit,
5249
+ { onEvent, signal, guardrails, parallel: parallelToolCalls }
5250
+ );
5251
+ toolEmit("result", result.map((m) => ({ role: m.role, content: m.parts.map((p) => p.value ?? "").join(""), metadata: m.metadata })));
5252
+ return result;
5011
5253
  });
5012
5254
  messages.push(...toolMessages2);
5013
- response = await executor.execute(agent, messages);
5255
+ emitEvent(onEvent, "messages_updated", { messages });
5014
5256
  continue;
5015
5257
  }
5016
- if (!hasToolCalls(response)) break;
5258
+ if (!hasToolCalls(response)) {
5259
+ const finalResult = options?.raw ? response : await process2(agent, response);
5260
+ if (guardrails) {
5261
+ const contentStr = typeof finalResult === "string" ? finalResult : JSON.stringify(finalResult);
5262
+ const assistantMsg = new Message("assistant", [text(contentStr)]);
5263
+ const gr = guardrails.checkOutput(assistantMsg);
5264
+ if (!gr.allowed) {
5265
+ emitEvent(onEvent, "error", { message: `Output guardrail denied: ${gr.reason}` });
5266
+ throw new GuardrailError(gr.reason ?? "Output guardrail denied");
5267
+ }
5268
+ if (gr.rewrite !== void 0) {
5269
+ emit("iterations", iteration);
5270
+ emit("result", gr.rewrite);
5271
+ emitEvent(onEvent, "done", { response: gr.rewrite, messages });
5272
+ return gr.rewrite;
5273
+ }
5274
+ }
5275
+ emit("iterations", iteration);
5276
+ emit("result", finalResult);
5277
+ emitEvent(onEvent, "done", { response: finalResult, messages });
5278
+ return finalResult;
5279
+ }
5280
+ if (guardrails) {
5281
+ const { textContent } = extractToolInfo(response);
5282
+ if (textContent) {
5283
+ const assistantMsg = new Message("assistant", [text(textContent)]);
5284
+ const gr = guardrails.checkOutput(assistantMsg);
5285
+ if (!gr.allowed) {
5286
+ emitEvent(onEvent, "error", { message: `Output guardrail denied: ${gr.reason}` });
5287
+ throw new GuardrailError(gr.reason ?? "Output guardrail denied");
5288
+ }
5289
+ }
5290
+ }
5017
5291
  iteration++;
5018
5292
  if (iteration > maxIterations) {
5019
5293
  throw new Error(
@@ -5021,24 +5295,64 @@ async function invokeAgent(prompt, inputs, options) {
5021
5295
  );
5022
5296
  }
5023
5297
  const toolMessages = await traceSpan("toolCalls", async (toolEmit) => {
5024
- toolEmit("signature", "prompty.invokeAgent.toolCalls");
5298
+ toolEmit("signature", "prompty.turn.toolCalls");
5025
5299
  toolEmit("description", `Tool call round ${iteration}`);
5026
- const result2 = await buildToolResultMessages(response, tools, agent, parentInputs, toolEmit);
5027
- toolEmit("result", result2.map((m) => ({ role: m.role, content: m.parts.map((p) => p.value ?? "").join(""), metadata: m.metadata })));
5028
- return result2;
5300
+ const result = await buildToolResultMessagesWithExtensions(
5301
+ response,
5302
+ tools,
5303
+ agent,
5304
+ parentInputs,
5305
+ toolEmit,
5306
+ { onEvent, signal, guardrails, parallel: parallelToolCalls }
5307
+ );
5308
+ toolEmit("result", result.map((m) => ({ role: m.role, content: m.parts.map((p) => p.value ?? "").join(""), metadata: m.metadata })));
5309
+ return result;
5029
5310
  });
5030
5311
  messages.push(...toolMessages);
5031
- response = await executor.execute(agent, messages);
5312
+ emitEvent(onEvent, "messages_updated", { messages });
5032
5313
  }
5033
- emit("iterations", iteration);
5034
- if (options?.raw) {
5035
- emit("result", response);
5036
- return response;
5037
- }
5038
- const result = await process2(agent, response);
5039
- emit("result", result);
5040
- return result;
5041
5314
  });
5315
+ if (options?.validator) {
5316
+ return cast(rawResult, options.validator);
5317
+ }
5318
+ return rawResult;
5319
+ }
5320
+ function resolveBindings(agent, toolName, args, parentInputs) {
5321
+ if (!parentInputs || !agent.tools || agent.tools.length === 0) return args;
5322
+ const toolDef = agent.tools.find((t) => t.name === toolName);
5323
+ if (!toolDef || !toolDef.bindings || toolDef.bindings.length === 0) return args;
5324
+ const merged = { ...args };
5325
+ for (const binding of toolDef.bindings) {
5326
+ if (binding.input in parentInputs) {
5327
+ merged[binding.name] = parentInputs[binding.input];
5328
+ }
5329
+ }
5330
+ return merged;
5331
+ }
5332
+ function isAsyncIterable(value) {
5333
+ return value != null && typeof value === "object" && Symbol.asyncIterator in value;
5334
+ }
5335
+ function isToolCallLike(item) {
5336
+ return typeof item === "object" && item !== null && "id" in item && "name" in item && "arguments" in item;
5337
+ }
5338
+ async function consumeStream(agent, response, onEvent) {
5339
+ const processed = await process2(agent, response);
5340
+ const toolCalls = [];
5341
+ const textParts = [];
5342
+ if (isAsyncIterable(processed)) {
5343
+ for await (const item of processed) {
5344
+ if (isToolCallLike(item)) {
5345
+ toolCalls.push(item);
5346
+ } else if (typeof item === "string") {
5347
+ textParts.push(item);
5348
+ emitEvent(onEvent, "token", { token: item });
5349
+ }
5350
+ }
5351
+ } else if (typeof processed === "string") {
5352
+ textParts.push(processed);
5353
+ emitEvent(onEvent, "token", { token: processed });
5354
+ }
5355
+ return { toolCalls, content: textParts.join("") };
5042
5356
  }
5043
5357
  function expandThreads(messages, nonces, inputs) {
5044
5358
  if (nonces.size === 0) return messages;
@@ -5107,160 +5421,138 @@ function hasToolCalls(response) {
5107
5421
  }
5108
5422
  return false;
5109
5423
  }
5110
- async function buildToolResultMessages(response, tools, agent, parentInputs, parentEmit) {
5424
+ function extractToolInfo(response) {
5425
+ if (typeof response !== "object" || response === null) {
5426
+ return { toolCalls: [], textContent: "" };
5427
+ }
5111
5428
  const r = response;
5112
5429
  if (Array.isArray(r.content) && r.stop_reason === "tool_use") {
5113
- return buildAnthropicToolResultMessages(r, tools, agent, parentInputs, parentEmit);
5430
+ const content = r.content;
5431
+ const toolCalls = content.filter((b) => b.type === "tool_use").map((b) => ({
5432
+ id: b.id,
5433
+ name: b.name,
5434
+ arguments: JSON.stringify(b.input)
5435
+ }));
5436
+ const textContent = content.filter((b) => b.type === "text").map((b) => b.text).join("");
5437
+ return { toolCalls, textContent };
5114
5438
  }
5115
5439
  if (r.object === "response" && Array.isArray(r.output)) {
5116
- return buildResponsesToolResultMessages(r, tools, agent, parentInputs, parentEmit);
5440
+ const funcCalls = r.output.filter(
5441
+ (item) => item.type === "function_call"
5442
+ );
5443
+ const toolCalls = funcCalls.map((fc) => ({
5444
+ id: fc.call_id ?? fc.id ?? "",
5445
+ call_id: fc.call_id ?? fc.id ?? "",
5446
+ name: fc.name,
5447
+ arguments: fc.arguments ?? "{}"
5448
+ }));
5449
+ return { toolCalls, textContent: "" };
5117
5450
  }
5118
- return buildOpenAIToolResultMessages(r, tools, agent, parentInputs, parentEmit);
5119
- }
5120
- async function buildOpenAIToolResultMessages(r, tools, agent, parentInputs, parentEmit) {
5121
5451
  const choices = r.choices;
5122
- const choice = choices[0];
5123
- const message = choice.message;
5124
- const toolCalls = message.tool_calls;
5125
- const messages = [];
5126
- const assistantContent = message.content ?? "";
5127
- messages.push(
5128
- new Message("assistant", assistantContent ? [text(assistantContent)] : [], {
5129
- tool_calls: toolCalls
5130
- })
5131
- );
5132
- const toolInputs = [];
5133
- for (const tc of toolCalls) {
5134
- const fn = tc.function;
5135
- const toolName = fn.name;
5136
- const toolCallId = tc.id;
5137
- let result;
5138
- let parsedArgs;
5139
- try {
5140
- parsedArgs = JSON.parse(fn.arguments);
5141
- if (agent && parentInputs && typeof parsedArgs === "object" && parsedArgs !== null && !Array.isArray(parsedArgs)) {
5142
- parsedArgs = resolveBindings(agent, toolName, parsedArgs, parentInputs);
5143
- }
5144
- result = await traceSpan(toolName, async (toolEmit) => {
5145
- toolEmit("signature", `prompty.tool.${toolName}`);
5146
- toolEmit("description", `Execute tool: ${toolName}`);
5147
- toolEmit("inputs", { arguments: parsedArgs, tool_call_id: toolCallId });
5148
- const r2 = await dispatchTool(toolName, parsedArgs, tools, agent ?? {}, parentInputs ?? {});
5149
- toolEmit("result", r2);
5150
- return r2;
5452
+ if (Array.isArray(choices) && choices.length > 0) {
5453
+ const choice = choices[0];
5454
+ const message = choice.message;
5455
+ if (message && Array.isArray(message.tool_calls)) {
5456
+ const toolCalls = message.tool_calls.map((tc) => {
5457
+ const fn = tc.function;
5458
+ return {
5459
+ id: tc.id,
5460
+ name: fn.name,
5461
+ arguments: fn.arguments
5462
+ };
5151
5463
  });
5152
- } catch (err) {
5153
- result = `Error: ${err instanceof Error ? err.message : String(err)}`;
5464
+ return { toolCalls, textContent: message.content ?? "" };
5154
5465
  }
5155
- toolInputs.push({ name: toolName, arguments: parsedArgs, tool_call_id: toolCallId, result });
5156
- messages.push(
5157
- new Message("tool", [text(result)], {
5158
- tool_call_id: toolCallId,
5159
- name: toolName
5160
- })
5161
- );
5162
- }
5163
- if (parentEmit) {
5164
- parentEmit("inputs", { tool_calls: toolInputs });
5165
5466
  }
5166
- return messages;
5467
+ return { toolCalls: [], textContent: "" };
5167
5468
  }
5168
- async function buildAnthropicToolResultMessages(r, tools, agent, parentInputs, parentEmit) {
5169
- const content = r.content;
5170
- const toolUseBlocks = content.filter((block) => block.type === "tool_use");
5171
- const messages = [];
5172
- const textParts = content.filter((block) => block.type === "text").map((block) => text(block.text));
5173
- messages.push(
5174
- new Message("assistant", textParts, { content })
5175
- );
5176
- const toolInputs = [];
5177
- const toolResultBlocks = [];
5178
- for (const block of toolUseBlocks) {
5179
- const toolName = block.name;
5180
- const toolCallId = block.id;
5181
- let toolArgs = block.input;
5182
- if (agent && parentInputs && typeof toolArgs === "object" && toolArgs !== null && !Array.isArray(toolArgs)) {
5183
- toolArgs = resolveBindings(agent, toolName, toolArgs, parentInputs);
5184
- }
5185
- let result;
5469
+ async function dispatchOneToolWithExtensions(tc, tools, agent, parentInputs, ext) {
5470
+ const { onEvent, signal, guardrails } = ext;
5471
+ try {
5472
+ checkCancellation(signal);
5473
+ } catch (err) {
5474
+ emitEvent(onEvent, "cancelled", {});
5475
+ throw err;
5476
+ }
5477
+ emitEvent(onEvent, "tool_call_start", { name: tc.name, arguments: tc.arguments });
5478
+ if (guardrails) {
5479
+ let parsedArgs2 = {};
5186
5480
  try {
5187
- result = await traceSpan(toolName, async (toolEmit) => {
5188
- toolEmit("signature", `prompty.tool.${toolName}`);
5189
- toolEmit("description", `Execute tool: ${toolName}`);
5190
- toolEmit("inputs", { arguments: toolArgs, tool_use_id: toolCallId });
5191
- const r2 = await dispatchTool(toolName, toolArgs, tools, agent ?? {}, parentInputs ?? {});
5192
- toolEmit("result", r2);
5193
- return r2;
5194
- });
5195
- } catch (err) {
5196
- result = `Error: ${err instanceof Error ? err.message : String(err)}`;
5481
+ const parsed = JSON.parse(tc.arguments);
5482
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
5483
+ parsedArgs2 = parsed;
5484
+ }
5485
+ } catch {
5486
+ }
5487
+ const gr = guardrails.checkTool(tc.name, parsedArgs2);
5488
+ if (!gr.allowed) {
5489
+ const deniedMsg = `Tool denied by guardrail: ${gr.reason}`;
5490
+ emitEvent(onEvent, "tool_result", { name: tc.name, result: deniedMsg });
5491
+ return deniedMsg;
5492
+ }
5493
+ if (gr.rewrite !== void 0) {
5494
+ tc = { ...tc, arguments: typeof gr.rewrite === "string" ? gr.rewrite : JSON.stringify(gr.rewrite) };
5197
5495
  }
5198
- toolInputs.push({ name: toolName, arguments: toolArgs, tool_use_id: toolCallId, result });
5199
- toolResultBlocks.push({
5200
- type: "tool_result",
5201
- tool_use_id: toolCallId,
5202
- content: result
5203
- });
5204
5496
  }
5205
- if (parentEmit) {
5206
- parentEmit("inputs", { tool_calls: toolInputs });
5497
+ let result;
5498
+ let parsedArgs;
5499
+ try {
5500
+ parsedArgs = JSON.parse(tc.arguments);
5501
+ if (agent && parentInputs && typeof parsedArgs === "object" && parsedArgs !== null && !Array.isArray(parsedArgs)) {
5502
+ parsedArgs = resolveBindings(agent, tc.name, parsedArgs, parentInputs);
5503
+ }
5504
+ result = await traceSpan(tc.name, async (toolEmit) => {
5505
+ toolEmit("signature", `prompty.tool.${tc.name}`);
5506
+ toolEmit("description", `Execute tool: ${tc.name}`);
5507
+ toolEmit("inputs", { arguments: parsedArgs, id: tc.id });
5508
+ const r = await dispatchTool(tc.name, parsedArgs, tools, agent, parentInputs);
5509
+ toolEmit("result", r);
5510
+ return r;
5511
+ });
5512
+ } catch (err) {
5513
+ if (err instanceof CancelledError) throw err;
5514
+ result = `Error: ${err instanceof Error ? err.message : String(err)}`;
5207
5515
  }
5208
- messages.push(
5209
- new Message("user", [], { tool_results: toolResultBlocks })
5210
- );
5211
- return messages;
5516
+ emitEvent(onEvent, "tool_result", { name: tc.name, result });
5517
+ return result;
5212
5518
  }
5213
- async function buildResponsesToolResultMessages(r, tools, agent, parentInputs, parentEmit) {
5214
- const output = r.output;
5215
- const funcCalls = output.filter((item) => item.type === "function_call");
5216
- const messages = [];
5217
- const toolInputs = [];
5218
- for (const fc of funcCalls) {
5219
- const toolName = fc.name;
5220
- const callId = fc.call_id ?? fc.id ?? "";
5221
- const argsStr = fc.arguments ?? "{}";
5222
- messages.push(
5223
- new Message("assistant", [], {
5224
- responses_function_call: {
5225
- type: "function_call",
5226
- call_id: callId,
5227
- name: toolName,
5228
- arguments: argsStr
5229
- }
5230
- })
5231
- );
5232
- let result;
5233
- let parsedArgs;
5234
- try {
5235
- parsedArgs = JSON.parse(argsStr);
5236
- if (agent && parentInputs && typeof parsedArgs === "object" && parsedArgs !== null && !Array.isArray(parsedArgs)) {
5237
- parsedArgs = resolveBindings(agent, toolName, parsedArgs, parentInputs);
5238
- }
5239
- result = await traceSpan(toolName, async (toolEmit) => {
5240
- toolEmit("signature", `prompty.tool.${toolName}`);
5241
- toolEmit("description", `Execute tool: ${toolName}`);
5242
- toolEmit("inputs", { arguments: parsedArgs, call_id: callId });
5243
- const r2 = await dispatchTool(toolName, parsedArgs, tools, agent ?? {}, parentInputs ?? {});
5244
- toolEmit("result", r2);
5245
- return r2;
5246
- });
5247
- } catch (err) {
5248
- result = `Error: ${err instanceof Error ? err.message : String(err)}`;
5249
- }
5250
- toolInputs.push({ name: toolName, arguments: parsedArgs, call_id: callId, result });
5251
- messages.push(
5252
- new Message("tool", [text(result)], {
5253
- tool_call_id: callId,
5254
- name: toolName
5255
- })
5519
+ async function dispatchToolsWithExtensions(toolCalls, tools, agent, parentInputs, ext) {
5520
+ if (ext.parallel && toolCalls.length > 1) {
5521
+ return Promise.all(
5522
+ toolCalls.map((tc) => dispatchOneToolWithExtensions(tc, tools, agent, parentInputs, ext))
5256
5523
  );
5257
5524
  }
5525
+ const results = [];
5526
+ for (const tc of toolCalls) {
5527
+ results.push(await dispatchOneToolWithExtensions(tc, tools, agent, parentInputs, ext));
5528
+ }
5529
+ return results;
5530
+ }
5531
+ async function buildToolResultMessagesWithExtensions(response, tools, agent, parentInputs, parentEmit, ext) {
5532
+ const { toolCalls, textContent } = extractToolInfo(response);
5533
+ const toolResults = await dispatchToolsWithExtensions(toolCalls, tools, agent, parentInputs, ext);
5258
5534
  if (parentEmit) {
5259
- parentEmit("inputs", { tool_calls: toolInputs });
5535
+ parentEmit("inputs", {
5536
+ tool_calls: toolCalls.map((tc, i) => ({ name: tc.name, arguments: tc.arguments, id: tc.id, result: toolResults[i] }))
5537
+ });
5538
+ }
5539
+ const provider = resolveProvider(agent);
5540
+ const executor = getExecutor(provider);
5541
+ return executor.formatToolMessages(response, toolCalls, toolResults, textContent);
5542
+ }
5543
+ async function buildToolMessagesFromCallsWithExtensions(toolCalls, textContent, tools, agent, parentInputs, parentEmit, ext) {
5544
+ const normalizedCalls = toolCalls.map((tc) => ({ id: tc.id, name: tc.name, arguments: tc.arguments }));
5545
+ const toolResults = await dispatchToolsWithExtensions(normalizedCalls, tools, agent, parentInputs, ext);
5546
+ if (parentEmit) {
5547
+ parentEmit("inputs", {
5548
+ tool_calls: normalizedCalls.map((tc, i) => ({ name: tc.name, arguments: tc.arguments, id: tc.id, result: toolResults[i] }))
5549
+ });
5260
5550
  }
5261
- return messages;
5551
+ const provider = resolveProvider(agent);
5552
+ const executor = getExecutor(provider);
5553
+ return executor.formatToolMessages(null, normalizedCalls, toolResults, textContent);
5262
5554
  }
5263
- var DEFAULT_FORMAT, DEFAULT_PARSER, DEFAULT_PROVIDER, DEFAULT_MAX_ITERATIONS;
5555
+ var DEFAULT_FORMAT, DEFAULT_PARSER, DEFAULT_PROVIDER, DEFAULT_MAX_ITERATIONS, invokeAgent;
5264
5556
  var init_pipeline = __esm({
5265
5557
  "src/core/pipeline.ts"() {
5266
5558
  "use strict";
@@ -5270,10 +5562,16 @@ var init_pipeline = __esm({
5270
5562
  init_tracer();
5271
5563
  init_loader();
5272
5564
  init_tool_dispatch();
5565
+ init_agent_events();
5566
+ init_cancellation();
5567
+ init_context2();
5568
+ init_guardrails();
5569
+ init_structured();
5273
5570
  DEFAULT_FORMAT = "nunjucks";
5274
5571
  DEFAULT_PARSER = "prompty";
5275
5572
  DEFAULT_PROVIDER = "openai";
5276
5573
  DEFAULT_MAX_ITERATIONS = 10;
5574
+ invokeAgent = turn;
5277
5575
  }
5278
5576
  });
5279
5577
 
@@ -5285,11 +5583,14 @@ __export(index_exports, {
5285
5583
  ApiKeyConnection: () => ApiKeyConnection,
5286
5584
  ArrayProperty: () => ArrayProperty,
5287
5585
  Binding: () => Binding,
5586
+ CancelledError: () => CancelledError,
5288
5587
  Connection: () => Connection,
5289
5588
  CustomTool: () => CustomTool,
5290
5589
  FormatConfig: () => FormatConfig,
5291
5590
  FoundryConnection: () => FoundryConnection,
5292
5591
  FunctionTool: () => FunctionTool,
5592
+ GuardrailError: () => GuardrailError,
5593
+ Guardrails: () => Guardrails,
5293
5594
  InvokerError: () => InvokerError,
5294
5595
  LoadContext: () => LoadContext,
5295
5596
  McpApprovalMode: () => McpApprovalMode,
@@ -5315,15 +5616,23 @@ __export(index_exports, {
5315
5616
  ReferenceConnection: () => ReferenceConnection,
5316
5617
  RemoteConnection: () => RemoteConnection,
5317
5618
  SaveContext: () => SaveContext,
5619
+ Steering: () => Steering,
5620
+ StructuredResultSymbol: () => StructuredResultSymbol,
5318
5621
  Template: () => Template,
5319
5622
  ThreadMarker: () => ThreadMarker,
5320
5623
  Tool: () => Tool,
5321
5624
  Tracer: () => Tracer,
5625
+ bindTools: () => bindTools,
5626
+ cast: () => cast,
5627
+ checkCancellation: () => checkCancellation,
5322
5628
  clearCache: () => clearCache,
5323
5629
  clearConnections: () => clearConnections,
5324
5630
  consoleTracer: () => consoleTracer,
5631
+ createStructuredResult: () => createStructuredResult,
5325
5632
  dictContentToPart: () => dictContentToPart,
5326
5633
  dictToMessage: () => dictToMessage,
5634
+ emitEvent: () => emitEvent,
5635
+ estimateChars: () => estimateChars,
5327
5636
  getConnection: () => getConnection,
5328
5637
  getExecutor: () => getExecutor,
5329
5638
  getParser: () => getParser,
@@ -5331,6 +5640,7 @@ __export(index_exports, {
5331
5640
  getRenderer: () => getRenderer,
5332
5641
  invoke: () => invoke,
5333
5642
  invokeAgent: () => invokeAgent,
5643
+ isStructuredResult: () => isStructuredResult,
5334
5644
  load: () => load,
5335
5645
  otelTracer: () => otelTracer,
5336
5646
  parse: () => parse,
@@ -5345,12 +5655,16 @@ __export(index_exports, {
5345
5655
  resolveBindings: () => resolveBindings,
5346
5656
  run: () => run,
5347
5657
  sanitizeValue: () => sanitizeValue,
5658
+ summarizeDropped: () => summarizeDropped,
5348
5659
  text: () => text,
5349
5660
  textMessage: () => textMessage,
5350
5661
  toSerializable: () => toSerializable,
5662
+ tool: () => tool,
5351
5663
  trace: () => trace,
5352
5664
  traceMethod: () => traceMethod,
5353
5665
  traceSpan: () => traceSpan,
5666
+ trimToContextWindow: () => trimToContextWindow,
5667
+ turn: () => turn,
5354
5668
  validateInputs: () => validateInputs
5355
5669
  });
5356
5670
  module.exports = __toCommonJS(index_exports);
@@ -5381,6 +5695,101 @@ function clearConnections() {
5381
5695
  init_loader();
5382
5696
  init_pipeline();
5383
5697
  init_tool_dispatch();
5698
+ init_agent_events();
5699
+ init_cancellation();
5700
+ init_context2();
5701
+ init_guardrails();
5702
+
5703
+ // src/core/steering.ts
5704
+ init_types();
5705
+ var Steering = class {
5706
+ queue = [];
5707
+ /** Enqueue a message to be injected at the next iteration. */
5708
+ send(message) {
5709
+ this.queue.push(message);
5710
+ }
5711
+ /** Remove and return all queued messages as Message objects. */
5712
+ drain() {
5713
+ const items = this.queue.splice(0);
5714
+ return items.map((text2) => new Message("user", [{ kind: "text", value: text2 }]));
5715
+ }
5716
+ /** Whether there are pending messages without consuming them. */
5717
+ get hasPending() {
5718
+ return this.queue.length > 0;
5719
+ }
5720
+ };
5721
+
5722
+ // src/core/tool-decorator.ts
5723
+ init_tool();
5724
+ init_property();
5725
+ init_tool_dispatch();
5726
+ function tool(fn, options) {
5727
+ const toolName = options?.name ?? fn.name;
5728
+ const toolDesc = options?.description ?? "";
5729
+ const shouldRegister = options?.register !== false;
5730
+ const properties = (options?.parameters ?? []).map(
5731
+ (p) => new Property({
5732
+ name: p.name,
5733
+ kind: p.kind ?? "string",
5734
+ required: p.required ?? p.default === void 0,
5735
+ description: p.description,
5736
+ default: p.default
5737
+ })
5738
+ );
5739
+ const toolDef = new FunctionTool({
5740
+ name: toolName,
5741
+ kind: "function",
5742
+ description: toolDesc,
5743
+ parameters: properties
5744
+ });
5745
+ const wrapped = fn;
5746
+ wrapped.__tool__ = toolDef;
5747
+ if (shouldRegister) {
5748
+ registerTool(toolName, fn);
5749
+ }
5750
+ return wrapped;
5751
+ }
5752
+ function bindTools(agent, tools) {
5753
+ const handlers = {};
5754
+ for (const fn of tools) {
5755
+ const toolDef = fn.__tool__;
5756
+ if (!toolDef) {
5757
+ throw new Error(
5758
+ `Function '${fn.name || "(anonymous)"}' is not a tool()-wrapped function (missing __tool__ property)`
5759
+ );
5760
+ }
5761
+ const name = toolDef.name;
5762
+ if (name in handlers) {
5763
+ throw new Error(`Duplicate tool handler: '${name}'`);
5764
+ }
5765
+ handlers[name] = fn;
5766
+ }
5767
+ const declaredFunctionTools = /* @__PURE__ */ new Set();
5768
+ for (const toolDef of agent.tools ?? []) {
5769
+ if (toolDef.kind === "function") {
5770
+ declaredFunctionTools.add(toolDef.name);
5771
+ }
5772
+ }
5773
+ for (const name of Object.keys(handlers)) {
5774
+ if (!declaredFunctionTools.has(name)) {
5775
+ const declared = [...declaredFunctionTools].sort().join(", ") || "(none)";
5776
+ throw new Error(
5777
+ `Tool handler '${name}' has no matching 'kind: function' declaration in agent.tools. Declared function tools: ${declared}`
5778
+ );
5779
+ }
5780
+ }
5781
+ for (const name of declaredFunctionTools) {
5782
+ if (!(name in handlers)) {
5783
+ console.warn(
5784
+ `Tool '${name}' is declared in agent.tools but no handler was provided to bindTools()`
5785
+ );
5786
+ }
5787
+ }
5788
+ return handlers;
5789
+ }
5790
+
5791
+ // src/core/index.ts
5792
+ init_structured();
5384
5793
 
5385
5794
  // src/renderers/nunjucks.ts
5386
5795
  var import_nunjucks = __toESM(require("nunjucks"), 1);
@@ -5774,11 +6183,14 @@ registerParser("prompty", new PromptyChatParser());
5774
6183
  ApiKeyConnection,
5775
6184
  ArrayProperty,
5776
6185
  Binding,
6186
+ CancelledError,
5777
6187
  Connection,
5778
6188
  CustomTool,
5779
6189
  FormatConfig,
5780
6190
  FoundryConnection,
5781
6191
  FunctionTool,
6192
+ GuardrailError,
6193
+ Guardrails,
5782
6194
  InvokerError,
5783
6195
  LoadContext,
5784
6196
  McpApprovalMode,
@@ -5804,15 +6216,23 @@ registerParser("prompty", new PromptyChatParser());
5804
6216
  ReferenceConnection,
5805
6217
  RemoteConnection,
5806
6218
  SaveContext,
6219
+ Steering,
6220
+ StructuredResultSymbol,
5807
6221
  Template,
5808
6222
  ThreadMarker,
5809
6223
  Tool,
5810
6224
  Tracer,
6225
+ bindTools,
6226
+ cast,
6227
+ checkCancellation,
5811
6228
  clearCache,
5812
6229
  clearConnections,
5813
6230
  consoleTracer,
6231
+ createStructuredResult,
5814
6232
  dictContentToPart,
5815
6233
  dictToMessage,
6234
+ emitEvent,
6235
+ estimateChars,
5816
6236
  getConnection,
5817
6237
  getExecutor,
5818
6238
  getParser,
@@ -5820,6 +6240,7 @@ registerParser("prompty", new PromptyChatParser());
5820
6240
  getRenderer,
5821
6241
  invoke,
5822
6242
  invokeAgent,
6243
+ isStructuredResult,
5823
6244
  load,
5824
6245
  otelTracer,
5825
6246
  parse,
@@ -5834,12 +6255,16 @@ registerParser("prompty", new PromptyChatParser());
5834
6255
  resolveBindings,
5835
6256
  run,
5836
6257
  sanitizeValue,
6258
+ summarizeDropped,
5837
6259
  text,
5838
6260
  textMessage,
5839
6261
  toSerializable,
6262
+ tool,
5840
6263
  trace,
5841
6264
  traceMethod,
5842
6265
  traceSpan,
6266
+ trimToContextWindow,
6267
+ turn,
5843
6268
  validateInputs
5844
6269
  });
5845
6270
  //# sourceMappingURL=index.cjs.map