@cognipeer/agent-sdk 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -260,7 +260,7 @@ type RuntimeProfile = BuiltInRuntimeProfile | "custom";
260
260
  type SummarizationMode = "incremental" | "full_rewrite";
261
261
  type ContextPolicy = "raw" | "summary_only" | "hybrid";
262
262
  type ToolResponseRetentionPolicy = "keep_full" | "keep_structured" | "summarize_archive" | "drop";
263
- type ToolResponseClassification = "critical" | "informative" | "verbose" | "redundant";
263
+ type ToolResponseClassification = "critical" | "small" | "informative" | "verbose" | "redundant";
264
264
  type PlanningMode = "off" | "todo" | "planner_executor" | "reasoning_then_tools";
265
265
  type ReplanPolicy = "never" | "on_failure" | "on_conflict" | "every_n_steps";
266
266
  type DelegationMode = "off" | "role_based" | "automatic";
@@ -366,15 +366,32 @@ type SmartAgentMemoryConfig = {
366
366
  readPolicy?: MemoryReadPolicy;
367
367
  };
368
368
  type SmartAgentToolResponseConfig = {
369
+ /** Upper threshold for marking a response as "verbose" (large). */
369
370
  maxToolResponseChars?: number;
371
+ /** Upper threshold (in approx. tokens) for marking a response as "verbose". */
370
372
  maxToolResponseTokens?: number;
373
+ /** Lower threshold for marking a response as "small". 0 disables the small tier. */
374
+ smallResponseChars?: number;
375
+ /** Policy applied to responses classified as "small". Defaults to keep_full. */
376
+ smallResponsePolicy?: ToolResponseRetentionPolicy;
377
+ /** Policy applied to mid-sized "informative" responses. */
371
378
  defaultPolicy?: ToolResponseRetentionPolicy;
379
+ /** Policy applied when a response exceeds the verbose thresholds. */
372
380
  largeResponsePolicy?: ToolResponseRetentionPolicy;
381
+ /** Per-tool override map. Wins over size-based classification. */
373
382
  toolResponseRetentionByTool?: Record<string, ToolResponseRetentionPolicy>;
383
+ /** Tool names always treated as critical (resolved via fallbackPolicy). */
374
384
  criticalTools?: string[];
375
385
  schemaValidation?: "strict" | "warn";
376
386
  retryOnSchemaError?: boolean;
387
+ /** Policy applied to critical tools. Should default to keep_full so critical payloads are never reduced. */
377
388
  fallbackPolicy?: ToolResponseRetentionPolicy;
389
+ /**
390
+ * Number of most-recent successful tool responses to render at full content
391
+ * regardless of their classification. Older responses still follow their
392
+ * resolved policy. 0 disables the recency override. Defaults to 2.
393
+ */
394
+ keepRecentFullCount?: number;
378
395
  };
379
396
  type SmartAgentCustomProfileConfig = {
380
397
  extends?: BuiltInRuntimeProfile;
package/dist/index.d.ts CHANGED
@@ -260,7 +260,7 @@ type RuntimeProfile = BuiltInRuntimeProfile | "custom";
260
260
  type SummarizationMode = "incremental" | "full_rewrite";
261
261
  type ContextPolicy = "raw" | "summary_only" | "hybrid";
262
262
  type ToolResponseRetentionPolicy = "keep_full" | "keep_structured" | "summarize_archive" | "drop";
263
- type ToolResponseClassification = "critical" | "informative" | "verbose" | "redundant";
263
+ type ToolResponseClassification = "critical" | "small" | "informative" | "verbose" | "redundant";
264
264
  type PlanningMode = "off" | "todo" | "planner_executor" | "reasoning_then_tools";
265
265
  type ReplanPolicy = "never" | "on_failure" | "on_conflict" | "every_n_steps";
266
266
  type DelegationMode = "off" | "role_based" | "automatic";
@@ -366,15 +366,32 @@ type SmartAgentMemoryConfig = {
366
366
  readPolicy?: MemoryReadPolicy;
367
367
  };
368
368
  type SmartAgentToolResponseConfig = {
369
+ /** Upper threshold for marking a response as "verbose" (large). */
369
370
  maxToolResponseChars?: number;
371
+ /** Upper threshold (in approx. tokens) for marking a response as "verbose". */
370
372
  maxToolResponseTokens?: number;
373
+ /** Lower threshold for marking a response as "small". 0 disables the small tier. */
374
+ smallResponseChars?: number;
375
+ /** Policy applied to responses classified as "small". Defaults to keep_full. */
376
+ smallResponsePolicy?: ToolResponseRetentionPolicy;
377
+ /** Policy applied to mid-sized "informative" responses. */
371
378
  defaultPolicy?: ToolResponseRetentionPolicy;
379
+ /** Policy applied when a response exceeds the verbose thresholds. */
372
380
  largeResponsePolicy?: ToolResponseRetentionPolicy;
381
+ /** Per-tool override map. Wins over size-based classification. */
373
382
  toolResponseRetentionByTool?: Record<string, ToolResponseRetentionPolicy>;
383
+ /** Tool names always treated as critical (resolved via fallbackPolicy). */
374
384
  criticalTools?: string[];
375
385
  schemaValidation?: "strict" | "warn";
376
386
  retryOnSchemaError?: boolean;
387
+ /** Policy applied to critical tools. Should default to keep_full so critical payloads are never reduced. */
377
388
  fallbackPolicy?: ToolResponseRetentionPolicy;
389
+ /**
390
+ * Number of most-recent successful tool responses to render at full content
391
+ * regardless of their classification. Older responses still follow their
392
+ * resolved policy. 0 disables the recency override. Defaults to 2.
393
+ */
394
+ keepRecentFullCount?: number;
378
395
  };
379
396
  type SmartAgentCustomProfileConfig = {
380
397
  extends?: BuiltInRuntimeProfile;
package/dist/index.mjs CHANGED
@@ -2128,13 +2128,16 @@ var BASE_DEFAULTS = {
2128
2128
  toolResponses: {
2129
2129
  maxToolResponseChars: 3600,
2130
2130
  maxToolResponseTokens: 1200,
2131
+ smallResponseChars: 0,
2132
+ smallResponsePolicy: "keep_full",
2131
2133
  defaultPolicy: "keep_structured",
2132
2134
  largeResponsePolicy: "summarize_archive",
2133
2135
  toolResponseRetentionByTool: {},
2134
2136
  criticalTools: [...DEFAULT_CRITICAL_TOOLS],
2135
2137
  schemaValidation: "strict",
2136
2138
  retryOnSchemaError: true,
2137
- fallbackPolicy: "keep_structured"
2139
+ fallbackPolicy: "keep_full",
2140
+ keepRecentFullCount: 0
2138
2141
  }
2139
2142
  };
2140
2143
  function buildProfile(overrides) {
@@ -2417,11 +2420,15 @@ function classifyToolResponse(toolName, output, config) {
2417
2420
  const tokenCount = countApproxTokens(serialized);
2418
2421
  const criticalTools = new Set(config.toolResponses.criticalTools);
2419
2422
  if (criticalTools.has(toolName)) return "critical";
2423
+ if (serialized === "ok" || serialized === "true" || serialized === "null") {
2424
+ return "redundant";
2425
+ }
2420
2426
  if (tokenCount >= config.toolResponses.maxToolResponseTokens || serialized.length >= config.toolResponses.maxToolResponseChars) {
2421
2427
  return "verbose";
2422
2428
  }
2423
- if (serialized === "ok" || serialized === "true" || serialized === "null") {
2424
- return "redundant";
2429
+ const smallChars = config.toolResponses.smallResponseChars;
2430
+ if (smallChars > 0 && serialized.length <= smallChars) {
2431
+ return "small";
2425
2432
  }
2426
2433
  return "informative";
2427
2434
  }
@@ -2431,12 +2438,14 @@ function resolveToolResponsePolicy(toolName, output, config) {
2431
2438
  const classification = classifyToolResponse(toolName, output, config);
2432
2439
  const byTool = config.toolResponses.toolResponseRetentionByTool[toolName];
2433
2440
  let retentionPolicy = byTool || config.toolResponses.defaultPolicy;
2434
- if (classification === "critical") {
2435
- retentionPolicy = byTool || config.toolResponses.fallbackPolicy;
2441
+ if (byTool) ; else if (classification === "critical") {
2442
+ retentionPolicy = config.toolResponses.fallbackPolicy;
2436
2443
  } else if (classification === "verbose") {
2437
- retentionPolicy = byTool || config.toolResponses.largeResponsePolicy;
2444
+ retentionPolicy = config.toolResponses.largeResponsePolicy;
2445
+ } else if (classification === "small") {
2446
+ retentionPolicy = config.toolResponses.smallResponsePolicy;
2438
2447
  } else if (classification === "redundant") {
2439
- retentionPolicy = byTool || "drop";
2448
+ retentionPolicy = "drop";
2440
2449
  }
2441
2450
  const summary = summarizeObject(output);
2442
2451
  if (retentionPolicy === "keep_full") {
@@ -2782,10 +2791,10 @@ function createToolsNode(initialTools, opts) {
2782
2791
  let toolMessageContent = responsePolicy.content;
2783
2792
  if (responsePolicy.retentionPolicy === "summarize_archive") {
2784
2793
  toolMessageContent = `ARCHIVED_TOOL_RESPONSE [executionId=${executionId}]: ${responsePolicy.summary}
2785
- Use get_tool_response with executionId "${executionId}" to retrieve the full output.`;
2794
+ Only use get_tool_response with executionId "${executionId}" if this summary is missing a specific field, id, snippet, or exact payload you still need.`;
2786
2795
  } else if (responsePolicy.retentionPolicy === "drop") {
2787
2796
  toolMessageContent = `DROPPED_TOOL_RESPONSE [executionId=${executionId}]: ${responsePolicy.summary}
2788
- Use get_tool_response with executionId "${executionId}" to retrieve the full output.`;
2797
+ Only use get_tool_response with executionId "${executionId}" if this summary is missing a specific field, id, snippet, or exact payload you still need.`;
2789
2798
  }
2790
2799
  appended.push({ role: "tool", content: toolMessageContent, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
2791
2800
  onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: output, durationMs });
@@ -3971,6 +3980,40 @@ function createAgentNode(opts) {
3971
3980
  }
3972
3981
 
3973
3982
  // src/smart/contextPolicy.ts
3983
+ function collectRecentFullToolCallIds(state, keepRecentFullCount) {
3984
+ if (!keepRecentFullCount || keepRecentFullCount <= 0) return /* @__PURE__ */ new Set();
3985
+ const history = state.toolHistory || [];
3986
+ const ids = [];
3987
+ for (let index = history.length - 1; index >= 0 && ids.length < keepRecentFullCount; index -= 1) {
3988
+ const entry = history[index];
3989
+ if (!entry || !entry.tool_call_id) continue;
3990
+ if (entry.status && entry.status !== "success") continue;
3991
+ ids.push(entry.tool_call_id);
3992
+ }
3993
+ return new Set(ids);
3994
+ }
3995
+ function applyRecentFullOverride(messages, state, config) {
3996
+ const keepN = config.toolResponses.keepRecentFullCount;
3997
+ if (!keepN || keepN <= 0) return messages;
3998
+ const recentIds = collectRecentFullToolCallIds(state, keepN);
3999
+ if (recentIds.size === 0) return messages;
4000
+ const history = state.toolHistory || [];
4001
+ const byCallId = /* @__PURE__ */ new Map();
4002
+ for (const entry of history) {
4003
+ if (entry?.tool_call_id) byCallId.set(entry.tool_call_id, entry);
4004
+ }
4005
+ return messages.map((message) => {
4006
+ if (message.role !== "tool" || !message.tool_call_id) return message;
4007
+ if (!recentIds.has(message.tool_call_id)) return message;
4008
+ const entry = byCallId.get(message.tool_call_id);
4009
+ if (!entry) return message;
4010
+ const raw = entry.rawOutput ?? entry.output;
4011
+ if (raw === void 0) return message;
4012
+ const fullContent = typeof raw === "string" ? raw : safeStringify(raw);
4013
+ if (!fullContent || fullContent === message.content) return message;
4014
+ return { ...message, content: fullContent };
4015
+ });
4016
+ }
3974
4017
  function collectRecentTurns(messages, lastTurnsToKeep) {
3975
4018
  if (lastTurnsToKeep <= 0) return [];
3976
4019
  const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
@@ -4071,7 +4114,7 @@ function clampToBudget(messages, maxContextTokens) {
4071
4114
  function buildModelMessages(state, config) {
4072
4115
  const rawMessages = state.messages || [];
4073
4116
  if (config.context.policy === "raw") {
4074
- return clampToBudget(rawMessages, config.limits.maxContextTokens);
4117
+ return clampToBudget(applyRecentFullOverride(rawMessages, state, config), config.limits.maxContextTokens);
4075
4118
  }
4076
4119
  const recentMessages = collectRecentTurns(rawMessages, config.context.lastTurnsToKeep);
4077
4120
  const latestSummary = state.summaryRecords?.[state.summaryRecords.length - 1];
@@ -4091,7 +4134,7 @@ function buildModelMessages(state, config) {
4091
4134
  ...syntheticContextMessages,
4092
4135
  ...config.context.policy === "summary_only" ? body.filter((message) => message.role === "user" || message.role === "assistant").slice(-2) : body
4093
4136
  ];
4094
- return clampToBudget(assembled, config.limits.maxContextTokens);
4137
+ return clampToBudget(applyRecentFullOverride(assembled, state, config), config.limits.maxContextTokens);
4095
4138
  }
4096
4139
 
4097
4140
  // src/nodes/contextSummarize.ts
@@ -4247,11 +4290,11 @@ function buildSummarizedToolMessage(message, state) {
4247
4290
  toolCallId ? `toolCallId=${toolCallId}` : null,
4248
4291
  executionId ? `executionId=${executionId}` : null
4249
4292
  ].filter(Boolean).join("; ");
4250
- let retrievalHint = "Use get_tool_response if exact details are still needed.";
4293
+ let retrievalHint = "Only use get_tool_response if this summary is missing a specific field, id, snippet, or exact payload you still need.";
4251
4294
  if (executionId && toolCallId) {
4252
- retrievalHint = `Use get_tool_response with executionId "${executionId}" or tool call id "${toolCallId}" if exact details are still needed.`;
4295
+ retrievalHint = `Only use get_tool_response with executionId "${executionId}" or tool call id "${toolCallId}" if this summary is missing a specific field, id, snippet, or exact payload you still need.`;
4253
4296
  } else if (executionId || toolCallId) {
4254
- retrievalHint = `Use get_tool_response with executionId "${executionId || toolCallId}" if exact details are still needed.`;
4297
+ retrievalHint = `Only use get_tool_response with executionId "${executionId || toolCallId}" if this summary is missing a specific field, id, snippet, or exact payload you still need.`;
4255
4298
  }
4256
4299
  return [
4257
4300
  `SUMMARIZED_TOOL_RESPONSE [${references}]`,
@@ -4317,11 +4360,13 @@ function createContextSummarizeNode(opts) {
4317
4360
  const onEvent = state.ctx?.__onEvent;
4318
4361
  const traceSession = state.ctx?.__traceSession;
4319
4362
  const toolResponsesConfigEarly = resolved.toolResponses;
4363
+ const recentFullToolCallIdsEarly = collectRecentFullToolCallIds(state, toolResponsesConfigEarly.keepRecentFullCount);
4320
4364
  const compressableMessages = messages.filter(
4321
4365
  (m) => {
4322
4366
  if (m.role !== "tool") return false;
4323
4367
  if (isSummarizedToolPlaceholder(m.content)) return false;
4324
4368
  if (isSyntheticSummaryMessage(m)) return false;
4369
+ if (m.tool_call_id && recentFullToolCallIdsEarly.has(m.tool_call_id)) return false;
4325
4370
  const histEntry = findToolHistoryEntry(state, m.tool_call_id, m.name);
4326
4371
  if (histEntry?.retentionPolicy === "keep_full") return false;
4327
4372
  if (!histEntry?.retentionPolicy) {
@@ -4508,6 +4553,7 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4508
4553
  }
4509
4554
  }
4510
4555
  const toolResponsesConfig = resolved.toolResponses;
4556
+ const recentFullToolCallIds = collectRecentFullToolCallIds(state, toolResponsesConfig.keepRecentFullCount);
4511
4557
  const newMessages = messages.map((m) => {
4512
4558
  if (m.role === "tool") {
4513
4559
  if (isSyntheticSummaryMessage(m)) {
@@ -4519,6 +4565,7 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4519
4565
  }
4520
4566
  if (isSummarizedToolPlaceholder(m.content)) return m;
4521
4567
  if (toolCallId && protectedToolCallIds.has(toolCallId)) return m;
4568
+ if (toolCallId && recentFullToolCallIds.has(toolCallId)) return m;
4522
4569
  const toolName = m.name || "";
4523
4570
  const historyEntry = findToolHistoryEntry(state, toolCallId, toolName);
4524
4571
  const entryRetention = historyEntry?.retentionPolicy;
@@ -4857,8 +4904,9 @@ function createContextTools(stateRef, opts) {
4857
4904
  }
4858
4905
  const getTool = createTool({
4859
4906
  name: "get_tool_response",
4860
- description: "RETRIEVE the full output of a tool execution whose response was archived, dropped, or later compacted in conversation history. 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.",
4907
+ 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.",
4861
4908
  schema: z.object({ executionId: z.string().describe("Tool execution id or original tool_call_id") }),
4909
+ maxExecutionsPerRun: 8,
4862
4910
  func: async ({ executionId }) => {
4863
4911
  const matchesExecution = (t) => t?.executionId === executionId || t?.tool_call_id === executionId;
4864
4912
  let execution = stateRef.toolHistory?.find((t) => matchesExecution(t));