@cognipeer/agent-sdk 0.6.2 → 0.6.3

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
@@ -627,6 +627,7 @@ function buildSystemPrompt(extra, planning, name = "Agent", todoListPrompt) {
627
627
  "- Keep privacy and safety: do not reveal secrets or sensitive data.",
628
628
  "- If inputs are ambiguous or missing, ask one concise clarifying question.",
629
629
  "- Reuse prior tool results already present in the conversation when sufficient.",
630
+ '- Use recovery tools such as "get_tool_response" only when the visible transcript explicitly shows a reduced tool-response marker (for example ARCHIVED_TOOL_RESPONSE, STRUCTURED_TOOL_RESPONSE, SUMMARIZED_TOOL_RESPONSE, DROPPED_TOOL_RESPONSE, or a truncation note with an executionId). Never use a recovery tool to re-fetch a normal tool result already visible in context.',
630
631
  planningBlock,
631
632
  extraTrimmed ? `Extra instructions: ${extraTrimmed}` : ""
632
633
  ].filter(Boolean).join("\n");
@@ -2552,6 +2553,7 @@ function createToolsNode(initialTools, opts) {
2552
2553
  anyTool._stateRef.toolHistory = state.toolHistory;
2553
2554
  anyTool._stateRef.toolHistoryArchived = state.toolHistoryArchived;
2554
2555
  anyTool._stateRef.pendingApprovals = pendingApprovals;
2556
+ anyTool._stateRef.messages = state.messages;
2555
2557
  anyTool._stateRef.ctx = state.ctx || (state.ctx = {});
2556
2558
  anyTool._stateRef.__onEvent = onEvent;
2557
2559
  }
@@ -4020,8 +4022,10 @@ function createAgent(opts) {
4020
4022
  agentRuntime: res.agent || runtime,
4021
4023
  status: "success"
4022
4024
  });
4023
- const finalAssistantMsg = getLastAssistantMessage(res.messages);
4024
- const content = extractMessageText2(finalAssistantMsg);
4025
+ const isTransientSummarizationExit = !!res.ctx?.__needsSummarization;
4026
+ const finalAssistantMsg = isTransientSummarizationExit ? void 0 : getLastAssistantMessage(res.messages);
4027
+ const finalIsSyntheticSummary = !isTransientSummarizationExit && finalAssistantMsg && isSyntheticSummaryMessage(finalAssistantMsg);
4028
+ const content = isTransientSummarizationExit || finalIsSyntheticSummary ? "" : extractMessageText2(finalAssistantMsg);
4025
4029
  let parsed = void 0;
4026
4030
  let outputError = void 0;
4027
4031
  if (soManager) {
@@ -4034,17 +4038,19 @@ function createAgent(opts) {
4034
4038
  } else {
4035
4039
  outputError = fallbackResult.error;
4036
4040
  }
4037
- } else {
4041
+ } else if (!isTransientSummarizationExit) {
4038
4042
  const noResult = soManager.noOutputResult(1);
4039
4043
  if (!noResult.success) {
4040
4044
  outputError = noResult.error;
4041
4045
  }
4042
4046
  }
4043
4047
  }
4044
- emit({ type: "finalAnswer", content });
4045
- if (streamEnabled && content) {
4046
- onStream?.({ text: content, isFinal: true });
4047
- emit({ type: "stream", text: content, isFinal: true });
4048
+ if (!isTransientSummarizationExit && !finalIsSyntheticSummary) {
4049
+ emit({ type: "finalAnswer", content });
4050
+ if (streamEnabled && content) {
4051
+ onStream?.({ text: content, isFinal: true });
4052
+ emit({ type: "stream", text: content, isFinal: true });
4053
+ }
4048
4054
  }
4049
4055
  return {
4050
4056
  content,
@@ -4842,6 +4848,13 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4842
4848
  };
4843
4849
  };
4844
4850
  }
4851
+ var TOOL_RESPONSE_RECOVERY_MARKERS = [
4852
+ /ARCHIVED_TOOL_RESPONSE\s*\[/,
4853
+ /STRUCTURED_TOOL_RESPONSE\s*\[/,
4854
+ /SUMMARIZED_TOOL_RESPONSE\b/,
4855
+ /DROPPED_TOOL_RESPONSE\s*\[/,
4856
+ /Use get_tool_response with executionId/i
4857
+ ];
4845
4858
  var todoStatusSchema = zod.z.enum(["not-started", "in-progress", "completed", "blocked"]);
4846
4859
  var todoWriteItemSchema = zod.z.object({
4847
4860
  id: zod.z.number().int().min(1).describe("Sequential id starting from 1"),
@@ -4910,180 +4923,223 @@ function formatTodoListSummary(todoList) {
4910
4923
  }
4911
4924
  return todoList.map((item) => `${item.id}. ${item.title || item.step || item.description || `Step ${item.id}`} [${item.status}]`).join("\n");
4912
4925
  }
4913
- function createContextTools(stateRef, opts) {
4914
- const tools = [];
4915
- if (opts?.planningEnabled) {
4916
- const manageTodo = createTool({
4917
- name: "manage_todo_list",
4918
- description: "Manage a structured todo list to track progress and plan tasks throughout your coding session.\n\nOperations:\n- read: return the current plan\n- write: replace the full plan with a complete ordered todoList\n- update: patch existing todo items by id without resending the whole plan\n\nRules:\n- Use write only when creating or fully rewriting the plan\n- After a plan exists, prefer update for status, evidence, or owner changes\n- Update payloads should contain only the changed items\n- When using update, pass expectedVersion to avoid overwriting a newer plan\n- Keep ids unique; write operations must keep ids sequential starting from 1\n- Keep at most ONE item in-progress at a time\n- If update fails due to version mismatch, read the latest plan and retry",
4919
- schema: zod.z.object({
4920
- operation: zod.z.enum(["write", "read", "update"]),
4921
- expectedVersion: zod.z.number().int().min(0).optional(),
4922
- todoList: zod.z.array(zod.z.union([todoWriteItemSchema, todoUpdateItemSchema])).optional()
4923
- }),
4924
- func: async ({ operation, expectedVersion, todoList }) => {
4925
- const toolStateRef = manageTodo._stateRef;
4926
- const onEvent = toolStateRef?.__onEvent;
4927
- const emitPlanEvent = (list) => {
4928
- const version = stateRef.planVersion || 1;
4929
- const adherenceScore = stateRef.adherenceScore || 0;
4930
- const planData = {
4931
- source: "manage_todo_list",
4926
+ function flattenRecoveryContent(content) {
4927
+ if (typeof content === "string") {
4928
+ return content;
4929
+ }
4930
+ if (Array.isArray(content)) {
4931
+ return content.map((part) => {
4932
+ if (typeof part === "string") {
4933
+ return part;
4934
+ }
4935
+ if (part && typeof part === "object") {
4936
+ if (typeof part.text === "string") {
4937
+ return part.text;
4938
+ }
4939
+ if (typeof part.content === "string") {
4940
+ return part.content;
4941
+ }
4942
+ }
4943
+ return "";
4944
+ }).filter(Boolean).join("\n");
4945
+ }
4946
+ return "";
4947
+ }
4948
+ function hasToolResponseRecoveryReference(messages, executionId) {
4949
+ if (!Array.isArray(messages) || messages.length === 0) {
4950
+ return false;
4951
+ }
4952
+ return messages.some((message) => {
4953
+ if (message?.role === "system") {
4954
+ return false;
4955
+ }
4956
+ const content = flattenRecoveryContent(message?.content);
4957
+ if (!content) {
4958
+ return false;
4959
+ }
4960
+ const hasRecoveryMarker = TOOL_RESPONSE_RECOVERY_MARKERS.some((pattern) => pattern.test(content));
4961
+ if (!hasRecoveryMarker) {
4962
+ return false;
4963
+ }
4964
+ return executionId ? content.includes(executionId) : true;
4965
+ });
4966
+ }
4967
+ function createManageTodoTool(stateRef) {
4968
+ const manageTodo = createTool({
4969
+ name: "manage_todo_list",
4970
+ description: "Manage a structured todo list to track progress and plan tasks throughout your coding session.\n\nOperations:\n- read: return the current plan\n- write: replace the full plan with a complete ordered todoList\n- update: patch existing todo items by id without resending the whole plan\n\nRules:\n- Use write only when creating or fully rewriting the plan\n- After a plan exists, prefer update for status, evidence, or owner changes\n- Update payloads should contain only the changed items\n- When using update, pass expectedVersion to avoid overwriting a newer plan\n- Keep ids unique; write operations must keep ids sequential starting from 1\n- Keep at most ONE item in-progress at a time\n- If update fails due to version mismatch, read the latest plan and retry",
4971
+ schema: zod.z.object({
4972
+ operation: zod.z.enum(["write", "read", "update"]),
4973
+ expectedVersion: zod.z.number().int().min(0).optional(),
4974
+ todoList: zod.z.array(zod.z.union([todoWriteItemSchema, todoUpdateItemSchema])).optional()
4975
+ }),
4976
+ func: async ({ operation, expectedVersion, todoList }) => {
4977
+ const toolStateRef = manageTodo._stateRef;
4978
+ const onEvent = toolStateRef?.__onEvent;
4979
+ const emitPlanEvent = (list) => {
4980
+ const version = stateRef.planVersion || 1;
4981
+ const adherenceScore = stateRef.adherenceScore || 0;
4982
+ const planData = {
4983
+ source: "manage_todo_list",
4984
+ operation,
4985
+ version,
4986
+ adherenceScore,
4987
+ count: Array.isArray(list) ? list.length : 0
4988
+ };
4989
+ onEvent?.({ type: "plan", todoList: list, ...planData });
4990
+ const traceSession = toolStateRef?.ctx?.__traceSession;
4991
+ recordTraceEvent(traceSession, {
4992
+ type: "plan",
4993
+ label: `Plan ${operation}`,
4994
+ actor: { scope: "agent", name: "manage_todo_list", role: "planner" },
4995
+ sections: [
4996
+ {
4997
+ kind: "summary",
4998
+ label: "Todo List",
4999
+ content: formatTodoListSummary(list)
5000
+ },
5001
+ {
5002
+ kind: "metadata",
5003
+ label: "Plan Metadata",
5004
+ data: planData
5005
+ }
5006
+ ],
5007
+ debug: planData
5008
+ });
5009
+ };
5010
+ const currentVersion = stateRef.planVersion || 0;
5011
+ if (operation === "read") {
5012
+ const list = stateRef.todoList || [];
5013
+ emitPlanEvent(list);
5014
+ return list;
5015
+ }
5016
+ if (typeof expectedVersion === "number" && expectedVersion !== currentVersion) {
5017
+ return {
5018
+ status: "error",
5019
+ operation,
5020
+ error: "Plan version mismatch. Read the latest plan and retry.",
5021
+ version: currentVersion,
5022
+ adherenceScore: stateRef.adherenceScore || 0
5023
+ };
5024
+ }
5025
+ if (!Array.isArray(todoList)) {
5026
+ return {
5027
+ status: "error",
5028
+ operation,
5029
+ error: "todoList is required for write and update operations.",
5030
+ version: currentVersion,
5031
+ adherenceScore: stateRef.adherenceScore || 0
5032
+ };
5033
+ }
5034
+ if (operation === "write") {
5035
+ const parsed = zod.z.array(todoWriteItemSchema).safeParse(todoList);
5036
+ if (!parsed.success) {
5037
+ return {
5038
+ status: "error",
4932
5039
  operation,
4933
- version,
4934
- adherenceScore,
4935
- count: Array.isArray(list) ? list.length : 0
5040
+ error: parsed.error.issues[0]?.message || "Invalid todoList for write.",
5041
+ version: currentVersion,
5042
+ adherenceScore: stateRef.adherenceScore || 0
4936
5043
  };
4937
- onEvent?.({ type: "plan", todoList: list, ...planData });
4938
- const traceSession = toolStateRef?.ctx?.__traceSession;
4939
- recordTraceEvent(traceSession, {
4940
- type: "plan",
4941
- label: `Plan ${operation}`,
4942
- actor: { scope: "agent", name: "manage_todo_list", role: "planner" },
4943
- sections: [
4944
- {
4945
- kind: "summary",
4946
- label: "Todo List",
4947
- content: formatTodoListSummary(list)
4948
- },
4949
- {
4950
- kind: "metadata",
4951
- label: "Plan Metadata",
4952
- data: planData
4953
- }
4954
- ],
4955
- debug: planData
4956
- });
4957
- };
4958
- const currentVersion = stateRef.planVersion || 0;
4959
- if (operation === "read") {
4960
- const list = stateRef.todoList || [];
4961
- emitPlanEvent(list);
4962
- return list;
4963
5044
  }
4964
- if (typeof expectedVersion === "number" && expectedVersion !== currentVersion) {
5045
+ const normalizedList = parsed.data.map((item) => normalizeTodoItem(item));
5046
+ const validation = validatePlanInvariants(normalizedList, true);
5047
+ if (!validation.ok) {
4965
5048
  return {
4966
5049
  status: "error",
4967
5050
  operation,
4968
- error: "Plan version mismatch. Read the latest plan and retry.",
5051
+ error: validation.error,
4969
5052
  version: currentVersion,
4970
5053
  adherenceScore: stateRef.adherenceScore || 0
4971
5054
  };
4972
5055
  }
4973
- if (!Array.isArray(todoList)) {
5056
+ stateRef.todoList = normalizedList;
5057
+ } else if (operation === "update") {
5058
+ const parsed = zod.z.array(todoUpdateItemSchema).safeParse(todoList);
5059
+ if (!parsed.success) {
4974
5060
  return {
4975
5061
  status: "error",
4976
5062
  operation,
4977
- error: "todoList is required for write and update operations.",
5063
+ error: parsed.error.issues[0]?.message || "Invalid todoList for update.",
4978
5064
  version: currentVersion,
4979
5065
  adherenceScore: stateRef.adherenceScore || 0
4980
5066
  };
4981
5067
  }
4982
- if (operation === "write") {
4983
- const parsed = zod.z.array(todoWriteItemSchema).safeParse(todoList);
4984
- if (!parsed.success) {
4985
- return {
4986
- status: "error",
4987
- operation,
4988
- error: parsed.error.issues[0]?.message || "Invalid todoList for write.",
4989
- version: currentVersion,
4990
- adherenceScore: stateRef.adherenceScore || 0
4991
- };
4992
- }
4993
- const normalizedList = parsed.data.map((item) => normalizeTodoItem(item));
4994
- const validation = validatePlanInvariants(normalizedList, true);
4995
- if (!validation.ok) {
4996
- return {
4997
- status: "error",
4998
- operation,
4999
- error: validation.error,
5000
- version: currentVersion,
5001
- adherenceScore: stateRef.adherenceScore || 0
5002
- };
5003
- }
5004
- stateRef.todoList = normalizedList;
5005
- } else if (operation === "update") {
5006
- const parsed = zod.z.array(todoUpdateItemSchema).safeParse(todoList);
5007
- if (!parsed.success) {
5008
- return {
5009
- status: "error",
5010
- operation,
5011
- error: parsed.error.issues[0]?.message || "Invalid todoList for update.",
5012
- version: currentVersion,
5013
- adherenceScore: stateRef.adherenceScore || 0
5014
- };
5015
- }
5016
- const currentList = Array.isArray(stateRef.todoList) ? stateRef.todoList : [];
5017
- if (currentList.length === 0) {
5068
+ const currentList = Array.isArray(stateRef.todoList) ? stateRef.todoList : [];
5069
+ if (currentList.length === 0) {
5070
+ return {
5071
+ status: "error",
5072
+ operation,
5073
+ error: "No existing plan to update. Use write first.",
5074
+ version: currentVersion,
5075
+ adherenceScore: stateRef.adherenceScore || 0
5076
+ };
5077
+ }
5078
+ const currentMap = new Map(currentList.map((item) => [item.id, item]));
5079
+ const patchMap = /* @__PURE__ */ new Map();
5080
+ for (const item of parsed.data) {
5081
+ if (patchMap.has(item.id)) {
5018
5082
  return {
5019
5083
  status: "error",
5020
5084
  operation,
5021
- error: "No existing plan to update. Use write first.",
5085
+ error: "Update payload contains duplicate todo ids.",
5022
5086
  version: currentVersion,
5023
5087
  adherenceScore: stateRef.adherenceScore || 0
5024
5088
  };
5025
5089
  }
5026
- const currentMap = new Map(currentList.map((item) => [item.id, item]));
5027
- const patchMap = /* @__PURE__ */ new Map();
5028
- for (const item of parsed.data) {
5029
- if (patchMap.has(item.id)) {
5030
- return {
5031
- status: "error",
5032
- operation,
5033
- error: "Update payload contains duplicate todo ids.",
5034
- version: currentVersion,
5035
- adherenceScore: stateRef.adherenceScore || 0
5036
- };
5037
- }
5038
- patchMap.set(item.id, item);
5039
- if (!currentMap.has(item.id)) {
5040
- return {
5041
- status: "error",
5042
- operation,
5043
- error: `Cannot update missing todo id ${item.id}. Use write to replace the plan.`,
5044
- version: currentVersion,
5045
- adherenceScore: stateRef.adherenceScore || 0
5046
- };
5047
- }
5048
- }
5049
- const mergedList = currentList.map((item) => {
5050
- const patch = patchMap.get(item.id);
5051
- return patch ? normalizeTodoItem(patch, item) : item;
5052
- });
5053
- const validation = validatePlanInvariants(mergedList, false);
5054
- if (!validation.ok) {
5090
+ patchMap.set(item.id, item);
5091
+ if (!currentMap.has(item.id)) {
5055
5092
  return {
5056
5093
  status: "error",
5057
5094
  operation,
5058
- error: validation.error,
5095
+ error: `Cannot update missing todo id ${item.id}. Use write to replace the plan.`,
5059
5096
  version: currentVersion,
5060
5097
  adherenceScore: stateRef.adherenceScore || 0
5061
5098
  };
5062
5099
  }
5063
- stateRef.todoList = mergedList;
5064
5100
  }
5065
- stateRef.planVersion = currentVersion + 1;
5066
- stateRef.adherenceScore = calculateAdherenceScore(stateRef.todoList || []);
5067
- const payload = {
5068
- status: "ok",
5069
- operation,
5070
- count: Array.isArray(todoList) ? todoList.length : void 0,
5071
- version: stateRef.planVersion || 1,
5072
- adherenceScore: stateRef.adherenceScore || 0
5073
- };
5074
- emitPlanEvent(stateRef.todoList || []);
5075
- return payload;
5076
- }
5077
- });
5078
- manageTodo._stateRef = stateRef;
5079
- tools.push(manageTodo);
5080
- }
5101
+ const mergedList = currentList.map((item) => {
5102
+ const patch = patchMap.get(item.id);
5103
+ return patch ? normalizeTodoItem(patch, item) : item;
5104
+ });
5105
+ const validation = validatePlanInvariants(mergedList, false);
5106
+ if (!validation.ok) {
5107
+ return {
5108
+ status: "error",
5109
+ operation,
5110
+ error: validation.error,
5111
+ version: currentVersion,
5112
+ adherenceScore: stateRef.adherenceScore || 0
5113
+ };
5114
+ }
5115
+ stateRef.todoList = mergedList;
5116
+ }
5117
+ stateRef.planVersion = currentVersion + 1;
5118
+ stateRef.adherenceScore = calculateAdherenceScore(stateRef.todoList || []);
5119
+ const payload = {
5120
+ status: "ok",
5121
+ operation,
5122
+ count: Array.isArray(todoList) ? todoList.length : void 0,
5123
+ version: stateRef.planVersion || 1,
5124
+ adherenceScore: stateRef.adherenceScore || 0
5125
+ };
5126
+ emitPlanEvent(stateRef.todoList || []);
5127
+ return payload;
5128
+ }
5129
+ });
5130
+ manageTodo._stateRef = stateRef;
5131
+ return manageTodo;
5132
+ }
5133
+ function createGetToolResponseTool(stateRef) {
5081
5134
  const getTool = createTool({
5082
5135
  name: "get_tool_response",
5083
- description: "RETRIEVE the full output of a tool execution whose response was archived, dropped, or later compacted in conversation history. Use this only when the preview or summary is missing a specific field, identifier, snippet, or exact payload you still need to finish the task. Do not call it reflexively for every archived response. When a tool response appears as 'ARCHIVED_TOOL_RESPONSE [executionId=xxx]', 'DROPPED_TOOL_RESPONSE [executionId=xxx]', or 'SUMMARIZED_TOOL_RESPONSE [...]', pass the referenced executionId to this tool to get the complete data. You can also pass the original tool_call_id from the tool call.",
5136
+ description: "RETRIEVE the full output of a tool execution whose response was archived, dropped, truncated, or later compacted in conversation history. This tool is only useful when the visible transcript already shows a reduced tool-response marker for the referenced executionId or tool_call_id. Use it only when a marker such as 'ARCHIVED_TOOL_RESPONSE', 'STRUCTURED_TOOL_RESPONSE', 'SUMMARIZED_TOOL_RESPONSE', 'DROPPED_TOOL_RESPONSE', or an explicit truncation note points you to a missing field you still need. Do not call it to re-fetch a normal tool result that is already present in context.",
5084
5137
  schema: zod.z.object({ executionId: zod.z.string().describe("Tool execution id or original tool_call_id") }),
5085
5138
  maxExecutionsPerRun: 8,
5086
5139
  func: async ({ executionId }) => {
5140
+ if (!hasToolResponseRecoveryReference(stateRef.messages, executionId)) {
5141
+ return "Execution not recoverable from the visible transcript. Use this only when the conversation already shows a reduced tool-response marker for that executionId.";
5142
+ }
5087
5143
  const matchesExecution = (t) => t?.executionId === executionId || t?.tool_call_id === executionId;
5088
5144
  let execution = stateRef.toolHistory?.find((t) => matchesExecution(t));
5089
5145
  if (!execution) {
@@ -5096,7 +5152,16 @@ function createContextTools(stateRef, opts) {
5096
5152
  }
5097
5153
  });
5098
5154
  getTool._stateRef = stateRef;
5099
- tools.push(getTool);
5155
+ return getTool;
5156
+ }
5157
+ function createContextTools(stateRef, opts) {
5158
+ const tools = [];
5159
+ if (opts?.planningEnabled) {
5160
+ tools.push(createManageTodoTool(stateRef));
5161
+ }
5162
+ if (opts?.includeGetToolResponse ?? true) {
5163
+ tools.push(createGetToolResponseTool(stateRef));
5164
+ }
5100
5165
  return tools;
5101
5166
  }
5102
5167
 
@@ -5264,9 +5329,11 @@ function createSmartAgent(opts) {
5264
5329
  useTodoList: planningEnabled
5265
5330
  };
5266
5331
  const stateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
5267
- const contextTools = createContextTools(stateRef, { planningEnabled });
5268
- const mergedTools = [...opts.tools ?? [], ...contextTools];
5269
- const base = createAgent({ ...runtimeOpts, tools: mergedTools });
5332
+ const contextTools = createContextTools(stateRef, { planningEnabled, includeGetToolResponse: false });
5333
+ const getToolResponseTool = createGetToolResponseTool(stateRef);
5334
+ const toolsWithoutRecovery = [...opts.tools ?? [], ...contextTools];
5335
+ const toolsWithRecovery = [...toolsWithoutRecovery, getToolResponseTool];
5336
+ const base = createAgent({ ...runtimeOpts, tools: toolsWithoutRecovery });
5270
5337
  base.__runtime.runtimeProfile = resolved.runtimeProfile;
5271
5338
  base.__runtime.smart = resolved;
5272
5339
  const summarizationEnabled = resolved.summarization.enable !== false;
@@ -5324,6 +5391,27 @@ function createSmartAgent(opts) {
5324
5391
  );
5325
5392
  return { role: "system", content: sys };
5326
5393
  }
5394
+ function syncRuntimeTools(currentState, currentMessages) {
5395
+ const needsRecoveryTool = hasToolResponseRecoveryReference(currentMessages);
5396
+ const nextTools = needsRecoveryTool ? toolsWithRecovery : toolsWithoutRecovery;
5397
+ const currentRuntime = currentState.agent || base.__runtime;
5398
+ if (currentRuntime.tools === nextTools) {
5399
+ return currentState;
5400
+ }
5401
+ return {
5402
+ ...currentState,
5403
+ agent: {
5404
+ ...currentRuntime,
5405
+ tools: nextTools
5406
+ }
5407
+ };
5408
+ }
5409
+ function producedTerminalAssistantTurn(messages) {
5410
+ const lastAssistant = [...messages].reverse().find((message) => message?.role === "assistant");
5411
+ if (!lastAssistant) return false;
5412
+ const toolCalls = Array.isArray(lastAssistant.tool_calls) ? lastAssistant.tool_calls : [];
5413
+ return toolCalls.length === 0;
5414
+ }
5327
5415
  async function trySummarize(currentState, currentRawMessages) {
5328
5416
  if (!summarizer) return { state: currentState, rawMessages: currentRawMessages, compressed: false };
5329
5417
  const delta = await summarizer(currentState);
@@ -5345,13 +5433,13 @@ function createSmartAgent(opts) {
5345
5433
  stateRef.adherenceScore = input.plan?.adherenceScore || 0;
5346
5434
  const alreadyHasSystem = Array.isArray(input.messages) && input.messages[0]?.role === "system";
5347
5435
  const seedMessages = alreadyHasSystem ? [...input.messages || []] : [systemMessage2(), ...input.messages || []];
5348
- let state = await syncMemory({ ...input, messages: seedMessages });
5436
+ let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages);
5349
5437
  let lastResult = null;
5350
5438
  let rawMessages = [...seedMessages];
5351
5439
  const effectiveMaxToolCalls = config?.limits?.maxToolCalls ?? resolved.limits.maxToolCalls ?? 10;
5352
5440
  const iterationLimit = Math.max(effectiveMaxToolCalls * 3 + 5, 30);
5353
5441
  for (let i = 0; i < iterationLimit; i++) {
5354
- state = { ...state, messages: rawMessages };
5442
+ state = syncRuntimeTools({ ...state, messages: rawMessages }, rawMessages);
5355
5443
  const next = summarizationEnabled ? decideBefore(state) : "agent";
5356
5444
  if (next === "contextSummarize" && summarizer) {
5357
5445
  const result = await trySummarize(state, rawMessages);
@@ -5378,7 +5466,7 @@ function createSmartAgent(opts) {
5378
5466
  if (currentPlan && !state.plan) {
5379
5467
  state = { ...state, plan: currentPlan };
5380
5468
  }
5381
- state = syncPlanState(await syncMemory(state));
5469
+ state = syncRuntimeTools(syncPlanState(await syncMemory(state)), rawMessages);
5382
5470
  stateRef.toolHistory = state.toolHistory;
5383
5471
  stateRef.toolHistoryArchived = state.toolHistoryArchived;
5384
5472
  if (state.ctx?.__needsSummarization && summarizer) {
@@ -5392,6 +5480,7 @@ function createSmartAgent(opts) {
5392
5480
  }
5393
5481
  if (state.ctx?.__finalizedDueToStructuredOutput) break;
5394
5482
  if (opts.outputSchema && lastResult?.output != null) break;
5483
+ if (producedTerminalAssistantTurn(appendedMessages)) break;
5395
5484
  if (summarizationEnabled) {
5396
5485
  const after = decideAfter(state);
5397
5486
  if (after === "contextSummarize" && summarizer) {
@@ -7691,8 +7780,10 @@ exports.createAgentNode = createAgentNode;
7691
7780
  exports.createCodeGuardrail = createCodeGuardrail;
7692
7781
  exports.createContextSummarizeNode = createContextSummarizeNode;
7693
7782
  exports.createContextTools = createContextTools;
7783
+ exports.createGetToolResponseTool = createGetToolResponseTool;
7694
7784
  exports.createGuardrail = createGuardrail;
7695
7785
  exports.createJsonGuardrail = createJsonGuardrail;
7786
+ exports.createManageTodoTool = createManageTodoTool;
7696
7787
  exports.createProvider = createProvider;
7697
7788
  exports.createRegexGuardrail = createRegexGuardrail;
7698
7789
  exports.createResolverNode = createResolverNode;
@@ -7714,6 +7805,7 @@ exports.generateTraceId = generateTraceId;
7714
7805
  exports.getModelCapabilities = getModelCapabilities;
7715
7806
  exports.getNestedValue = getNestedValue;
7716
7807
  exports.getResolvedSmartConfig = getResolvedSmartConfig;
7808
+ exports.hasToolResponseRecoveryReference = hasToolResponseRecoveryReference;
7717
7809
  exports.httpSink = httpSink;
7718
7810
  exports.isSmartModel = isSmartModel;
7719
7811
  exports.jsonSchemaRule = jsonSchemaRule;