@full-self-browsing/lattice 1.3.0-rc.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,17 +1,6 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-CiIaOW0V.js";
1
2
  import mime from "mime";
2
3
  import canonicalize from "canonicalize";
3
- //#region \0rolldown/runtime.js
4
- var __defProp = Object.defineProperty;
5
- var __exportAll = (all, no_symbols) => {
6
- let target = {};
7
- for (var name in all) __defProp(target, name, {
8
- get: all[name],
9
- enumerable: true
10
- });
11
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
12
- return target;
13
- };
14
- //#endregion
15
4
  //#region src/artifacts/metadata.ts
16
5
  const textEncoder$1 = new TextEncoder();
17
6
  function inferMediaType(value, options) {
@@ -446,13 +435,16 @@ async function createReceipt(input, signer) {
446
435
  const receiptId = input.receiptId ?? crypto.randomUUID();
447
436
  const issuedAt = input.issuedAt ?? (/* @__PURE__ */ new Date()).toISOString();
448
437
  const { body } = redactReceiptBody({
449
- version: "lattice-receipt/v1.1",
438
+ version: "lattice-receipt/v1.3",
450
439
  receiptId,
451
440
  runId: input.runId,
452
441
  issuedAt,
453
442
  kid: signer.kid,
454
443
  model: input.model,
455
444
  route: input.route,
445
+ ...input.modelClass !== void 0 ? { modelClass: input.modelClass } : {},
446
+ ...input.parentReceiptCid !== void 0 ? { parentReceiptCid: input.parentReceiptCid } : {},
447
+ ...input.lineageMerkleRoot !== void 0 ? { lineageMerkleRoot: input.lineageMerkleRoot } : {},
456
448
  usage: usageToCanonical(input.usage),
457
449
  contractVerdict: input.contractVerdict,
458
450
  contractHash: input.contractHash,
@@ -597,51 +589,164 @@ function extractReceiptId(envelope) {
597
589
  }
598
590
  }
599
591
  //#endregion
600
- //#region src/runtime/survivability.ts
592
+ //#region src/agent/format-tools.ts
601
593
  /**
602
- * Reference implementation of SurvivabilityAdapter<TState>. Records
603
- * eviction events but does NOT persist; serialize / deserialize round-
604
- * trip via JSON.stringify / JSON.parse. Analog to createFakeProvider
605
- * in the providers/ module -- gives Lattice's vitest a complete shape-
606
- * conformance target before the real (FSB-side) adapter ships in
607
- * Plan 05-05.
608
- *
609
- * Per CONTEXT.md D-11 the noop adapter ships in Lattice (not FSB)
610
- * because it covers the contract surface in Lattice's own test suite;
611
- * FSB's real chrome.storage.session-backed adapter is glue layer.
594
+ * Convert a Standard Schema to a JSON Schema-shaped descriptor suitable for
595
+ * inclusion in an LLM tool description. Standard Schema vendors can
596
+ * optionally expose `toJSONSchema` on their schema objects; when absent,
597
+ * we fall back to a minimal structural description that lists the schema
598
+ * vendor + version + a placeholder. Models tolerate placeholder schemas in
599
+ * practice because the tool description is supplementary — what matters
600
+ * is the envelope contract (`{tool_call: {name, args}}`).
612
601
  */
613
- function createNoopSurvivabilityAdapter(options = {}) {
614
- const id = options.id ?? "noop-survivability";
615
- const defaultPolicy = options.policy ?? "SAFE";
616
- const hooks = /* @__PURE__ */ new Set();
602
+ function toolSchemaToJsonSchema(schema) {
603
+ const standardSchema = schema["~standard"];
604
+ if (typeof standardSchema === "object" && standardSchema !== null && "vendor" in standardSchema) {
605
+ const vendor = standardSchema;
606
+ const maybeToJson = schema.toJSONSchema;
607
+ if (typeof maybeToJson === "function") try {
608
+ return maybeToJson();
609
+ } catch {}
610
+ return {
611
+ $comment: `standard-schema vendor: ${vendor.vendor}; toJSONSchema not available`,
612
+ type: "object"
613
+ };
614
+ }
617
615
  return {
618
- kind: "survivability-adapter",
619
- id,
620
- serialize(state) {
621
- return {
622
- kind: "survivability-snapshot",
623
- version: "lattice-survivability/v1",
624
- payload: JSON.stringify(state ?? null),
625
- capturedAt: (/* @__PURE__ */ new Date()).toISOString()
626
- };
627
- },
628
- deserialize(snapshot) {
629
- return JSON.parse(snapshot.payload);
630
- },
631
- onEviction(hook) {
632
- hooks.add(hook);
633
- let unsubscribed = false;
634
- return () => {
635
- if (unsubscribed) return;
636
- unsubscribed = true;
637
- hooks.delete(hook);
638
- };
639
- },
640
- async resume(_snapshot) {
641
- return defaultPolicy;
616
+ $comment: "non-standard-schema input",
617
+ type: "object"
618
+ };
619
+ }
620
+ /**
621
+ * Builds the prompt-reencoded tool-use protocol handle for any provider.
622
+ *
623
+ * Phase 19 ships a uniform implementation across all 7 logical providers
624
+ * (openai, openai-compat, anthropic, gemini, xai, openrouter, lm-studio).
625
+ * The `providerName` argument is accepted for forward compatibility but
626
+ * does not branch the implementation in v1.2.
627
+ */
628
+ function formatToolsForProvider(providerName, tools, options = {}) {
629
+ options.mode;
630
+ const system = options.system?.trim() ?? "";
631
+ const toolDescriptions = tools.map((tool) => {
632
+ const schemaDescriptor = toolSchemaToJsonSchema(tool.inputSchema);
633
+ const schemaJson = JSON.stringify(schemaDescriptor, null, 2);
634
+ const desc = tool.description?.trim() ?? "(no description)";
635
+ return `- name: ${tool.name}\n description: ${desc}\n args_schema: ${schemaJson}`;
636
+ }).join("\n");
637
+ const envelopeInstructions = [
638
+ "You are a single-agent loop. You can either:",
639
+ " (a) answer the user directly with a final response, OR",
640
+ " (b) request one or more tool calls.",
641
+ "",
642
+ "To request tool calls, respond with ONE JSON object on a line by itself:",
643
+ " {\"tool_calls\": [{\"id\": \"...\", \"name\": \"tool_name\", \"args\": {...}}]}",
644
+ "Each tool_call needs a unique id (any string). The args MUST match the tool's args_schema.",
645
+ "",
646
+ "To answer directly, respond with a final answer in natural language with NO JSON envelope.",
647
+ "Do not mix a final answer and a tool_calls envelope in the same response."
648
+ ].join("\n");
649
+ const systemBlock = [
650
+ system,
651
+ "",
652
+ "Available tools:",
653
+ toolDescriptions || "(none)",
654
+ "",
655
+ envelopeInstructions
656
+ ].filter((s) => s !== "" || true).join("\n").replace(/^\n+/, "").trimEnd();
657
+ function assembleTask(conversation, includeSystemBlock) {
658
+ const lines = [];
659
+ if (includeSystemBlock) lines.push(systemBlock);
660
+ lines.push("");
661
+ lines.push("---");
662
+ lines.push("");
663
+ for (const turn of conversation) {
664
+ if (turn.role === "user") lines.push(`USER:\n${turn.content}`);
665
+ else if (turn.role === "assistant") lines.push(`ASSISTANT:\n${turn.content}`);
666
+ else {
667
+ const idHint = turn.toolCallId !== void 0 ? ` id=${turn.toolCallId}` : "";
668
+ const nameHint = turn.toolName !== void 0 ? ` name=${turn.toolName}` : "";
669
+ lines.push(`TOOL_RESULT (${nameHint.trim() || "tool"}${idHint}):\n${turn.content}`);
670
+ }
671
+ lines.push("");
642
672
  }
673
+ lines.push("ASSISTANT:");
674
+ return lines.join("\n");
675
+ }
676
+ function buildTask(conversation) {
677
+ return assembleTask(conversation, true);
678
+ }
679
+ function buildTaskBody(conversation) {
680
+ return assembleTask(conversation, false);
681
+ }
682
+ function describeForSystem() {
683
+ return systemBlock;
684
+ }
685
+ return {
686
+ buildTask,
687
+ buildTaskBody,
688
+ parseToolUse: parseToolUseEnvelope,
689
+ describeForSystem,
690
+ mode: "prompt-reencoded"
643
691
  };
644
692
  }
693
+ function parseToolUseEnvelope(responseText) {
694
+ if (typeof responseText !== "string" || responseText.length === 0) return null;
695
+ const candidates = extractJsonCandidates(responseText);
696
+ for (const candidate of candidates) {
697
+ const parsed = tryParseEnvelope(candidate);
698
+ if (parsed !== null) return parsed;
699
+ }
700
+ return null;
701
+ }
702
+ /**
703
+ * Extracts JSON-looking candidate substrings from a response text.
704
+ *
705
+ * Models routinely wrap JSON in markdown code fences (```json ... ```),
706
+ * prepend explanatory prose ("I'll call the search tool: { ... }"), or
707
+ * produce multiple JSON-shaped blobs. This extractor scans for plausible
708
+ * candidates ordered by likelihood.
709
+ */
710
+ function extractJsonCandidates(text) {
711
+ const candidates = [];
712
+ const fenceRegex = /```(?:json)?\s*([\s\S]*?)```/g;
713
+ let fenceMatch;
714
+ while ((fenceMatch = fenceRegex.exec(text)) !== null) {
715
+ const inner = fenceMatch[1];
716
+ if (inner !== void 0) candidates.push(inner.trim());
717
+ }
718
+ const braceStart = text.indexOf("{");
719
+ const braceEnd = text.lastIndexOf("}");
720
+ if (braceStart !== -1 && braceEnd > braceStart) candidates.push(text.slice(braceStart, braceEnd + 1));
721
+ candidates.push(text.trim());
722
+ return candidates;
723
+ }
724
+ function tryParseEnvelope(jsonLike) {
725
+ let parsed;
726
+ try {
727
+ parsed = JSON.parse(jsonLike);
728
+ } catch {
729
+ return null;
730
+ }
731
+ if (typeof parsed !== "object" || parsed === null) return null;
732
+ const toolCalls = parsed["tool_calls"];
733
+ if (!Array.isArray(toolCalls) || toolCalls.length === 0) return null;
734
+ const requests = [];
735
+ for (const call of toolCalls) {
736
+ if (typeof call !== "object" || call === null) return null;
737
+ const callRecord = call;
738
+ const id = callRecord["id"];
739
+ const name = callRecord["name"];
740
+ const args = callRecord["args"];
741
+ if (typeof id !== "string" || typeof name !== "string") return null;
742
+ requests.push({
743
+ id,
744
+ name,
745
+ args
746
+ });
747
+ }
748
+ return requests;
749
+ }
645
750
  //#endregion
646
751
  //#region src/outputs/validate.ts
647
752
  async function validateSchemaOutput(name, schema, value) {
@@ -771,6 +876,52 @@ function normalizePathKey(key) {
771
876
  return key;
772
877
  }
773
878
  //#endregion
879
+ //#region src/runtime/survivability.ts
880
+ /**
881
+ * Reference implementation of SurvivabilityAdapter<TState>. Records
882
+ * eviction events but does NOT persist; serialize / deserialize round-
883
+ * trip via JSON.stringify / JSON.parse. Analog to createFakeProvider
884
+ * in the providers/ module -- gives Lattice's vitest a complete shape-
885
+ * conformance target before the real (FSB-side) adapter ships in
886
+ * Plan 05-05.
887
+ *
888
+ * Per CONTEXT.md D-11 the noop adapter ships in Lattice (not FSB)
889
+ * because it covers the contract surface in Lattice's own test suite;
890
+ * FSB's real chrome.storage.session-backed adapter is glue layer.
891
+ */
892
+ function createNoopSurvivabilityAdapter(options = {}) {
893
+ const id = options.id ?? "noop-survivability";
894
+ const defaultPolicy = options.policy ?? "SAFE";
895
+ const hooks = /* @__PURE__ */ new Set();
896
+ return {
897
+ kind: "survivability-adapter",
898
+ id,
899
+ serialize(state) {
900
+ return {
901
+ kind: "survivability-snapshot",
902
+ version: "lattice-survivability/v1",
903
+ payload: JSON.stringify(state ?? null),
904
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString()
905
+ };
906
+ },
907
+ deserialize(snapshot) {
908
+ return JSON.parse(snapshot.payload);
909
+ },
910
+ onEviction(hook) {
911
+ hooks.add(hook);
912
+ let unsubscribed = false;
913
+ return () => {
914
+ if (unsubscribed) return;
915
+ unsubscribed = true;
916
+ hooks.delete(hook);
917
+ };
918
+ },
919
+ async resume(_snapshot) {
920
+ return defaultPolicy;
921
+ }
922
+ };
923
+ }
924
+ //#endregion
774
925
  //#region src/tools/tools.ts
775
926
  function defineTool(definition) {
776
927
  return {
@@ -815,158 +966,6 @@ function createToolCallId() {
815
966
  return `${Date.now()}:${Math.random().toString(16).slice(2)}`;
816
967
  }
817
968
  //#endregion
818
- //#region src/agent/format-tools.ts
819
- /**
820
- * Convert a Standard Schema to a JSON Schema-shaped descriptor suitable for
821
- * inclusion in an LLM tool description. Standard Schema vendors can
822
- * optionally expose `toJSONSchema` on their schema objects; when absent,
823
- * we fall back to a minimal structural description that lists the schema
824
- * vendor + version + a placeholder. Models tolerate placeholder schemas in
825
- * practice because the tool description is supplementary — what matters
826
- * is the envelope contract (`{tool_call: {name, args}}`).
827
- */
828
- function toolSchemaToJsonSchema(schema) {
829
- const standardSchema = schema["~standard"];
830
- if (typeof standardSchema === "object" && standardSchema !== null && "vendor" in standardSchema) {
831
- const vendor = standardSchema;
832
- const maybeToJson = schema.toJSONSchema;
833
- if (typeof maybeToJson === "function") try {
834
- return maybeToJson();
835
- } catch {}
836
- return {
837
- $comment: `standard-schema vendor: ${vendor.vendor}; toJSONSchema not available`,
838
- type: "object"
839
- };
840
- }
841
- return {
842
- $comment: "non-standard-schema input",
843
- type: "object"
844
- };
845
- }
846
- /**
847
- * Builds the prompt-reencoded tool-use protocol handle for any provider.
848
- *
849
- * Phase 19 ships a uniform implementation across all 7 logical providers
850
- * (openai, openai-compat, anthropic, gemini, xai, openrouter, lm-studio).
851
- * The `providerName` argument is accepted for forward compatibility but
852
- * does not branch the implementation in v1.2.
853
- */
854
- function formatToolsForProvider(providerName, tools, options = {}) {
855
- options.mode;
856
- const system = options.system?.trim() ?? "";
857
- const toolDescriptions = tools.map((tool) => {
858
- const schemaDescriptor = toolSchemaToJsonSchema(tool.inputSchema);
859
- const schemaJson = JSON.stringify(schemaDescriptor, null, 2);
860
- const desc = tool.description?.trim() ?? "(no description)";
861
- return `- name: ${tool.name}\n description: ${desc}\n args_schema: ${schemaJson}`;
862
- }).join("\n");
863
- const envelopeInstructions = [
864
- "You are a single-agent loop. You can either:",
865
- " (a) answer the user directly with a final response, OR",
866
- " (b) request one or more tool calls.",
867
- "",
868
- "To request tool calls, respond with ONE JSON object on a line by itself:",
869
- " {\"tool_calls\": [{\"id\": \"...\", \"name\": \"tool_name\", \"args\": {...}}]}",
870
- "Each tool_call needs a unique id (any string). The args MUST match the tool's args_schema.",
871
- "",
872
- "To answer directly, respond with a final answer in natural language with NO JSON envelope.",
873
- "Do not mix a final answer and a tool_calls envelope in the same response."
874
- ].join("\n");
875
- const systemBlock = [
876
- system,
877
- "",
878
- "Available tools:",
879
- toolDescriptions || "(none)",
880
- "",
881
- envelopeInstructions
882
- ].filter((s) => s !== "" || true).join("\n").replace(/^\n+/, "").trimEnd();
883
- function buildTask(conversation) {
884
- const lines = [];
885
- lines.push(systemBlock);
886
- lines.push("");
887
- lines.push("---");
888
- lines.push("");
889
- for (const turn of conversation) {
890
- if (turn.role === "user") lines.push(`USER:\n${turn.content}`);
891
- else if (turn.role === "assistant") lines.push(`ASSISTANT:\n${turn.content}`);
892
- else {
893
- const idHint = turn.toolCallId !== void 0 ? ` id=${turn.toolCallId}` : "";
894
- const nameHint = turn.toolName !== void 0 ? ` name=${turn.toolName}` : "";
895
- lines.push(`TOOL_RESULT (${nameHint.trim() || "tool"}${idHint}):\n${turn.content}`);
896
- }
897
- lines.push("");
898
- }
899
- lines.push("ASSISTANT:");
900
- return lines.join("\n");
901
- }
902
- function parseToolUse(responseText) {
903
- if (typeof responseText !== "string" || responseText.length === 0) return null;
904
- const candidates = extractJsonCandidates(responseText);
905
- for (const candidate of candidates) {
906
- const parsed = tryParseEnvelope(candidate);
907
- if (parsed !== null) return parsed;
908
- }
909
- return null;
910
- }
911
- function describeForSystem() {
912
- return systemBlock;
913
- }
914
- return {
915
- buildTask,
916
- parseToolUse,
917
- describeForSystem,
918
- mode: "prompt-reencoded"
919
- };
920
- }
921
- /**
922
- * Extracts JSON-looking candidate substrings from a response text.
923
- *
924
- * Models routinely wrap JSON in markdown code fences (```json ... ```),
925
- * prepend explanatory prose ("I'll call the search tool: { ... }"), or
926
- * produce multiple JSON-shaped blobs. This extractor scans for plausible
927
- * candidates ordered by likelihood.
928
- */
929
- function extractJsonCandidates(text) {
930
- const candidates = [];
931
- const fenceRegex = /```(?:json)?\s*([\s\S]*?)```/g;
932
- let fenceMatch;
933
- while ((fenceMatch = fenceRegex.exec(text)) !== null) {
934
- const inner = fenceMatch[1];
935
- if (inner !== void 0) candidates.push(inner.trim());
936
- }
937
- const braceStart = text.indexOf("{");
938
- const braceEnd = text.lastIndexOf("}");
939
- if (braceStart !== -1 && braceEnd > braceStart) candidates.push(text.slice(braceStart, braceEnd + 1));
940
- candidates.push(text.trim());
941
- return candidates;
942
- }
943
- function tryParseEnvelope(jsonLike) {
944
- let parsed;
945
- try {
946
- parsed = JSON.parse(jsonLike);
947
- } catch {
948
- return null;
949
- }
950
- if (typeof parsed !== "object" || parsed === null) return null;
951
- const toolCalls = parsed["tool_calls"];
952
- if (!Array.isArray(toolCalls) || toolCalls.length === 0) return null;
953
- const requests = [];
954
- for (const call of toolCalls) {
955
- if (typeof call !== "object" || call === null) return null;
956
- const callRecord = call;
957
- const id = callRecord["id"];
958
- const name = callRecord["name"];
959
- const args = callRecord["args"];
960
- if (typeof id !== "string" || typeof name !== "string") return null;
961
- requests.push({
962
- id,
963
- name,
964
- args
965
- });
966
- }
967
- return requests;
968
- }
969
- //#endregion
970
969
  //#region src/agent/host.ts
971
970
  /**
972
971
  * Reference implementation suitable for Node tests + the Phase 19 default
@@ -1020,7 +1019,10 @@ var AgentDeniedError = class extends Error {
1020
1019
  };
1021
1020
  //#endregion
1022
1021
  //#region src/agent/runtime.ts
1023
- var runtime_exports = /* @__PURE__ */ __exportAll({ runAgent: () => runAgent });
1022
+ var runtime_exports = /* @__PURE__ */ __exportAll({
1023
+ runAgent: () => runAgent,
1024
+ runAgentInternal: () => runAgentInternal
1025
+ });
1024
1026
  const ZERO_USAGE = {
1025
1027
  promptTokens: 0,
1026
1028
  completionTokens: 0,
@@ -1033,8 +1035,20 @@ const ZERO_USAGE = {
1033
1035
  * calling Promise), direct transport (provider.execute()), and in-memory
1034
1036
  * transcript (the `conversation` array). Phase 20 promotes scheduler /
1035
1037
  * transport / storage to the pluggable `AgentHost` adapter.
1038
+ *
1039
+ * Phase 39: `runAgent` is a thin public wrapper over `runAgentInternal`
1040
+ * with no internal options — the public signature and behavior are
1041
+ * unchanged.
1036
1042
  */
1037
1043
  async function runAgent(intent, config = {}) {
1044
+ return runAgentInternal(intent, config);
1045
+ }
1046
+ /**
1047
+ * The agent-loop implementation with the internal dispatch seam (Phase 39).
1048
+ * In-package consumers (agent/crew/, 39-05) call this directly; it is NOT
1049
+ * part of the public package surface.
1050
+ */
1051
+ async function runAgentInternal(intent, config = {}, internalOptions = {}) {
1038
1052
  const startedAt = Date.now();
1039
1053
  const cumulativeUsage = {
1040
1054
  promptTokens: 0,
@@ -1169,8 +1183,12 @@ async function runAgent(intent, config = {}) {
1169
1183
  const iterUsage = response.normalizedUsage ?? ZERO_USAGE;
1170
1184
  accumulateUsage(cumulativeUsage, iterUsage);
1171
1185
  const responseText = extractResponseText(response);
1172
- const toolUseRequests = handle.parseToolUse(responseText);
1173
- if (toolUseRequests === null) {
1186
+ const toolUseRequests = response.toolCalls !== void 0 ? response.toolCalls.map((toolCall) => ({
1187
+ id: toolCall.id,
1188
+ name: toolCall.name,
1189
+ args: toolCall.args
1190
+ })) : handle.parseToolUse(responseText);
1191
+ if (toolUseRequests === null || toolUseRequests.length === 0) {
1174
1192
  const finalRecord = {
1175
1193
  index: iterationIndex,
1176
1194
  provider: providerName,
@@ -1195,9 +1213,11 @@ async function runAgent(intent, config = {}) {
1195
1213
  previousStepName: `agent-iteration-${iterationIndex}-before`
1196
1214
  });
1197
1215
  await host.storage?.clear();
1216
+ const artifactRefs = response.artifactRefs !== void 0 ? response.artifactRefs.map(toArtifactRef) : [];
1198
1217
  return {
1199
1218
  kind: "success",
1200
1219
  output: { answer: responseText },
1220
+ ...artifactRefs.length > 0 ? { artifacts: artifactRefs } : {},
1201
1221
  usage: snapshotUsage(cumulativeUsage),
1202
1222
  iterations: Object.freeze([...iterations])
1203
1223
  };
@@ -1208,28 +1228,41 @@ async function runAgent(intent, config = {}) {
1208
1228
  });
1209
1229
  const toolCallRecords = [];
1210
1230
  for (const req of toolUseRequests) {
1211
- const tool = intent.tools.find((t) => t.name === req.name);
1212
- let toolResult = null;
1213
- let resultContent;
1231
+ let resultContent = null;
1214
1232
  let resultHash = "tool-not-found";
1215
- if (tool === void 0) resultContent = JSON.stringify({ error: `Unknown tool: ${req.name}` });
1216
- else try {
1217
- await pipeline.run("BEFORE_TOOL", {
1233
+ if (internalOptions.dispatchToolUse !== void 0) {
1234
+ const dispatched = await internalOptions.dispatchToolUse(req, {
1218
1235
  iterationIndex,
1219
- toolName: req.name,
1220
- args: req.args
1236
+ conversation,
1237
+ pipeline
1221
1238
  });
1222
- toolResult = await runTool(tool, req.args);
1223
- resultContent = stringifyArtifactValue(toolResult.artifact.value);
1224
- resultHash = toolResult.callId;
1225
- await pipeline.run("AFTER_TOOL", {
1226
- iterationIndex,
1227
- toolName: req.name,
1228
- args: req.args,
1229
- result: toolResult.artifact.value
1230
- });
1231
- } catch (error) {
1232
- resultContent = JSON.stringify({ error: error instanceof Error ? error.message : "Tool execution failed" });
1239
+ if (dispatched !== void 0) {
1240
+ resultContent = dispatched.content;
1241
+ resultHash = stableHash(dispatched.content);
1242
+ }
1243
+ }
1244
+ if (resultContent === null) {
1245
+ const tool = intent.tools.find((t) => t.name === req.name);
1246
+ let toolResult = null;
1247
+ if (tool === void 0) resultContent = JSON.stringify({ error: `Unknown tool: ${req.name}` });
1248
+ else try {
1249
+ await pipeline.run("BEFORE_TOOL", {
1250
+ iterationIndex,
1251
+ toolName: req.name,
1252
+ args: req.args
1253
+ });
1254
+ toolResult = await runTool(tool, req.args);
1255
+ resultContent = stringifyArtifactValue(toolResult.artifact.value);
1256
+ resultHash = toolResult.callId;
1257
+ await pipeline.run("AFTER_TOOL", {
1258
+ iterationIndex,
1259
+ toolName: req.name,
1260
+ args: req.args,
1261
+ result: toolResult.artifact.value
1262
+ });
1263
+ } catch (error) {
1264
+ resultContent = JSON.stringify({ error: error instanceof Error ? error.message : "Tool execution failed" });
1265
+ }
1233
1266
  }
1234
1267
  conversation.push({
1235
1268
  role: "tool",
@@ -1354,6 +1387,6 @@ function stableHash(input) {
1354
1387
  }
1355
1388
  }
1356
1389
  //#endregion
1357
- export { createHookPipeline as C, BAND as S, toArtifactRef as T, PAYLOAD_TYPE as _, formatToolsForProvider as a, decodeEnvelope as b, importMcpTools as c, validateOutputMap as d, createNoopSurvivabilityAdapter as f, createReceipt as g, createCheckpointHook as h, createNoopAgentHost as i, runTool as l, STEP_TRANSITION_EVENT_NAME as m, runtime_exports as n, toolSchemaToJsonSchema as o, DEFAULT_CHECKPOINT_BAND as p, AgentDeniedError as r, defineTool as s, runAgent as t, toolArtifactRef as u, base64Encode as v, artifact as w, canonicalizeReceiptBody as x, buildPae as y };
1390
+ export { decodeEnvelope as C, artifact as D, createHookPipeline as E, isArtifactRef as O, buildPae as S, BAND as T, STEP_TRANSITION_EVENT_NAME as _, createNoopAgentHost as a, PAYLOAD_TYPE as b, runTool as c, validateOutputMap as d, validateSchemaOutput as f, DEFAULT_CHECKPOINT_BAND as g, toolSchemaToJsonSchema as h, AgentDeniedError as i, toArtifactRef as k, toolArtifactRef as l, parseToolUseEnvelope as m, runAgentInternal as n, defineTool as o, formatToolsForProvider as p, runtime_exports as r, importMcpTools as s, runAgent as t, createNoopSurvivabilityAdapter as u, createCheckpointHook as v, canonicalizeReceiptBody as w, base64Encode as x, createReceipt as y };
1358
1391
 
1359
- //# sourceMappingURL=runtime-CfZ-sGGk.js.map
1392
+ //# sourceMappingURL=runtime-D25ehzCj.js.map