@cognipeer/agent-sdk 0.5.4 → 0.6.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.
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" | "small" | "informative" | "verbose" | "redundant";
263
+ type ToolResponseClassification = "critical" | "informative" | "verbose";
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,32 +366,30 @@ type SmartAgentMemoryConfig = {
366
366
  readPolicy?: MemoryReadPolicy;
367
367
  };
368
368
  type SmartAgentToolResponseConfig = {
369
- /** Upper threshold for marking a response as "verbose" (large). */
369
+ /**
370
+ * Hard cap (in characters) applied at tool execution time to a single response.
371
+ * Anything larger is truncated immediately with a get_tool_response retrieval hint
372
+ * so a single oversized payload cannot blow up the next model call. Critical
373
+ * tools are exempt from this cap.
374
+ */
370
375
  maxToolResponseChars?: number;
371
- /** Upper threshold (in approx. tokens) for marking a response as "verbose". */
376
+ /** Hard cap (approx. tokens) for the same eager truncation rule. */
372
377
  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. */
378
+ /**
379
+ * Retention policy applied to all non-critical tool responses by the summarizer
380
+ * when it fires (context limit reached). Has no effect at execution time.
381
+ * Defaults to "summarize_archive".
382
+ */
378
383
  defaultPolicy?: ToolResponseRetentionPolicy;
379
- /** Policy applied when a response exceeds the verbose thresholds. */
380
- largeResponsePolicy?: ToolResponseRetentionPolicy;
381
- /** Per-tool override map. Wins over size-based classification. */
384
+ /**
385
+ * Per-tool override of the summarizer retention policy. Wins over `defaultPolicy`.
386
+ * Critical tools cannot be reduced regardless of override.
387
+ */
382
388
  toolResponseRetentionByTool?: Record<string, ToolResponseRetentionPolicy>;
383
- /** Tool names always treated as critical (resolved via fallbackPolicy). */
389
+ /** Tool names whose responses are never reduced by the summarizer or hard cap. */
384
390
  criticalTools?: string[];
385
391
  schemaValidation?: "strict" | "warn";
386
392
  retryOnSchemaError?: boolean;
387
- /** Policy applied to critical tools. Should default to keep_full so critical payloads are never reduced. */
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;
395
393
  };
396
394
  type SmartAgentCustomProfileConfig = {
397
395
  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" | "small" | "informative" | "verbose" | "redundant";
263
+ type ToolResponseClassification = "critical" | "informative" | "verbose";
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,32 +366,30 @@ type SmartAgentMemoryConfig = {
366
366
  readPolicy?: MemoryReadPolicy;
367
367
  };
368
368
  type SmartAgentToolResponseConfig = {
369
- /** Upper threshold for marking a response as "verbose" (large). */
369
+ /**
370
+ * Hard cap (in characters) applied at tool execution time to a single response.
371
+ * Anything larger is truncated immediately with a get_tool_response retrieval hint
372
+ * so a single oversized payload cannot blow up the next model call. Critical
373
+ * tools are exempt from this cap.
374
+ */
370
375
  maxToolResponseChars?: number;
371
- /** Upper threshold (in approx. tokens) for marking a response as "verbose". */
376
+ /** Hard cap (approx. tokens) for the same eager truncation rule. */
372
377
  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. */
378
+ /**
379
+ * Retention policy applied to all non-critical tool responses by the summarizer
380
+ * when it fires (context limit reached). Has no effect at execution time.
381
+ * Defaults to "summarize_archive".
382
+ */
378
383
  defaultPolicy?: ToolResponseRetentionPolicy;
379
- /** Policy applied when a response exceeds the verbose thresholds. */
380
- largeResponsePolicy?: ToolResponseRetentionPolicy;
381
- /** Per-tool override map. Wins over size-based classification. */
384
+ /**
385
+ * Per-tool override of the summarizer retention policy. Wins over `defaultPolicy`.
386
+ * Critical tools cannot be reduced regardless of override.
387
+ */
382
388
  toolResponseRetentionByTool?: Record<string, ToolResponseRetentionPolicy>;
383
- /** Tool names always treated as critical (resolved via fallbackPolicy). */
389
+ /** Tool names whose responses are never reduced by the summarizer or hard cap. */
384
390
  criticalTools?: string[];
385
391
  schemaValidation?: "strict" | "warn";
386
392
  retryOnSchemaError?: boolean;
387
- /** Policy applied to critical tools. Should default to keep_full so critical payloads are never reduced. */
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;
395
393
  };
396
394
  type SmartAgentCustomProfileConfig = {
397
395
  extends?: BuiltInRuntimeProfile;
package/dist/index.mjs CHANGED
@@ -2126,18 +2126,13 @@ var BASE_DEFAULTS = {
2126
2126
  requireJsonOutputContract: true
2127
2127
  },
2128
2128
  toolResponses: {
2129
- maxToolResponseChars: 3600,
2130
- maxToolResponseTokens: 1200,
2131
- smallResponseChars: 0,
2132
- smallResponsePolicy: "keep_full",
2133
- defaultPolicy: "keep_structured",
2134
- largeResponsePolicy: "summarize_archive",
2129
+ maxToolResponseChars: 12e3,
2130
+ maxToolResponseTokens: 3e3,
2131
+ defaultPolicy: "summarize_archive",
2135
2132
  toolResponseRetentionByTool: {},
2136
2133
  criticalTools: [...DEFAULT_CRITICAL_TOOLS],
2137
2134
  schemaValidation: "strict",
2138
- retryOnSchemaError: true,
2139
- fallbackPolicy: "keep_full",
2140
- keepRecentFullCount: 0
2135
+ retryOnSchemaError: true
2141
2136
  }
2142
2137
  };
2143
2138
  function buildProfile(overrides) {
@@ -2176,7 +2171,7 @@ var DEFAULT_PROFILE_CONFIGS = {
2176
2171
  },
2177
2172
  memory: { readPolicy: "recent_only" },
2178
2173
  delegation: { mode: "off", maxDelegationDepth: 1, maxChildCalls: 2, maxParallelChild: 1, childContextPolicy: "minimal" },
2179
- toolResponses: { maxToolResponseChars: 2400, maxToolResponseTokens: 800 }
2174
+ toolResponses: { maxToolResponseChars: 8e3, maxToolResponseTokens: 2e3 }
2180
2175
  }),
2181
2176
  balanced: buildProfile({}),
2182
2177
  deep: buildProfile({
@@ -2193,7 +2188,7 @@ var DEFAULT_PROFILE_CONFIGS = {
2193
2188
  },
2194
2189
  memory: { scope: "workspace" },
2195
2190
  delegation: { maxDelegationDepth: 3, maxChildCalls: 6 },
2196
- toolResponses: { maxToolResponseChars: 5400, maxToolResponseTokens: 1800 }
2191
+ toolResponses: { maxToolResponseChars: 16e3, maxToolResponseTokens: 4e3 }
2197
2192
  }),
2198
2193
  research: buildProfile({
2199
2194
  limits: { maxToolCalls: 20, maxParallelTools: 4, maxContextTokens: 56e3 },
@@ -2209,7 +2204,7 @@ var DEFAULT_PROFILE_CONFIGS = {
2209
2204
  },
2210
2205
  memory: { scope: "workspace" },
2211
2206
  delegation: { mode: "automatic", maxDelegationDepth: 4, maxChildCalls: 8, maxParallelChild: 3, childContextPolicy: "full" },
2212
- toolResponses: { maxToolResponseChars: 7200, maxToolResponseTokens: 2200 }
2207
+ toolResponses: { maxToolResponseChars: 24e3, maxToolResponseTokens: 6e3 }
2213
2208
  })
2214
2209
  };
2215
2210
  function mergeLimits(base, override) {
@@ -2416,63 +2411,65 @@ function summarizeObject(value) {
2416
2411
  return safeStringify(value);
2417
2412
  }
2418
2413
  function classifyToolResponse(toolName, output, config) {
2419
- const serialized = safeStringify(output);
2420
- const tokenCount = countApproxTokens(serialized);
2421
2414
  const criticalTools = new Set(config.toolResponses.criticalTools);
2422
2415
  if (criticalTools.has(toolName)) return "critical";
2423
- if (serialized === "ok" || serialized === "true" || serialized === "null") {
2424
- return "redundant";
2425
- }
2416
+ const serialized = safeStringify(output);
2417
+ const tokenCount = countApproxTokens(serialized);
2426
2418
  if (tokenCount >= config.toolResponses.maxToolResponseTokens || serialized.length >= config.toolResponses.maxToolResponseChars) {
2427
2419
  return "verbose";
2428
2420
  }
2429
- const smallChars = config.toolResponses.smallResponseChars;
2430
- if (smallChars > 0 && serialized.length <= smallChars) {
2431
- return "small";
2432
- }
2433
2421
  return "informative";
2434
2422
  }
2435
- function resolveToolResponsePolicy(toolName, output, config) {
2423
+ function applyToolResponseHardCap(toolName, output, executionId, config) {
2436
2424
  const serialized = safeStringify(output);
2437
2425
  const tokenCount = countApproxTokens(serialized);
2438
2426
  const classification = classifyToolResponse(toolName, output, config);
2439
- const byTool = config.toolResponses.toolResponseRetentionByTool[toolName];
2440
- let retentionPolicy = byTool || config.toolResponses.defaultPolicy;
2441
- if (byTool) ; else if (classification === "critical") {
2442
- retentionPolicy = config.toolResponses.fallbackPolicy;
2443
- } else if (classification === "verbose") {
2444
- retentionPolicy = config.toolResponses.largeResponsePolicy;
2445
- } else if (classification === "small") {
2446
- retentionPolicy = config.toolResponses.smallResponsePolicy;
2447
- } else if (classification === "redundant") {
2448
- retentionPolicy = "drop";
2449
- }
2450
- const summary = summarizeObject(output);
2451
- if (retentionPolicy === "keep_full") {
2452
- return { classification, retentionPolicy, content: serialized, rawOutput: output, summary, tokenCount };
2453
- }
2454
- if (retentionPolicy === "keep_structured") {
2455
- return { classification, retentionPolicy, content: summary, rawOutput: output, summary, tokenCount };
2456
- }
2457
- if (retentionPolicy === "summarize_archive") {
2458
- return {
2459
- classification,
2460
- retentionPolicy,
2461
- content: `ARCHIVED_TOOL_RESPONSE: ${summary}`,
2462
- rawOutput: output,
2463
- summary,
2464
- tokenCount
2465
- };
2427
+ if (classification !== "verbose") {
2428
+ return { classification, content: serialized, rawOutput: output, tokenCount, truncated: false };
2466
2429
  }
2430
+ const cap = config.toolResponses.maxToolResponseChars;
2431
+ const head = serialized.slice(0, cap);
2432
+ const note = `
2433
+ ... [TRUNCATED ${serialized.length - cap} chars by hard cap. Use get_tool_response with executionId="${executionId}" to fetch the full payload.]`;
2467
2434
  return {
2468
2435
  classification,
2469
- retentionPolicy,
2470
- content: `DROPPED_TOOL_RESPONSE: ${summary}`,
2436
+ content: head + note,
2471
2437
  rawOutput: output,
2472
- summary,
2473
- tokenCount
2438
+ tokenCount,
2439
+ truncated: true
2474
2440
  };
2475
2441
  }
2442
+ function resolveSummarizationRetention(toolName, config) {
2443
+ const criticalTools = new Set(config.toolResponses.criticalTools);
2444
+ if (criticalTools.has(toolName)) return "keep_full";
2445
+ const byTool = config.toolResponses.toolResponseRetentionByTool[toolName];
2446
+ return byTool || config.toolResponses.defaultPolicy;
2447
+ }
2448
+ function renderRetainedToolMessage(args) {
2449
+ const { policy, rawOutput, toolName, toolCallId, executionId, prebuiltSummary } = args;
2450
+ if (policy === "keep_full") {
2451
+ return typeof rawOutput === "string" ? rawOutput : safeStringify(rawOutput);
2452
+ }
2453
+ const summary = prebuiltSummary || summarizeObject(rawOutput);
2454
+ const refs = [
2455
+ `toolName=${toolName}`,
2456
+ toolCallId ? `toolCallId=${toolCallId}` : null,
2457
+ executionId ? `executionId=${executionId}` : null
2458
+ ].filter(Boolean).join("; ");
2459
+ const refId = executionId || toolCallId || "";
2460
+ if (policy === "keep_structured") {
2461
+ return `STRUCTURED_TOOL_RESPONSE [${refs}]
2462
+ Preview: ${summary}
2463
+ Use get_tool_response with executionId "${refId}" to fetch the full payload when a specific field is needed.`;
2464
+ }
2465
+ if (policy === "summarize_archive") {
2466
+ return `ARCHIVED_TOOL_RESPONSE [${refs}]
2467
+ Summary: ${summary}
2468
+ Use get_tool_response with executionId "${refId}" to fetch the full payload.`;
2469
+ }
2470
+ return `DROPPED_TOOL_RESPONSE [${refs}]
2471
+ Use get_tool_response with executionId "${refId}" only if you must recover the original payload.`;
2472
+ }
2476
2473
  function validateToolArgs(tool, args) {
2477
2474
  const schema = tool.schema;
2478
2475
  if (!schema || typeof schema.parse !== "function") {
@@ -2751,52 +2748,25 @@ function createToolsNode(initialTools, opts) {
2751
2748
  state.ctx.__structuredOutputParsed = output.data;
2752
2749
  state.ctx.__finalizedDueToStructuredOutput = true;
2753
2750
  }
2754
- const responsePolicy = resolveToolResponsePolicy(toolName, output, resolved);
2751
+ const responsePolicy = applyToolResponseHardCap(toolName, output, executionId, resolved);
2755
2752
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2756
- const storedOutput = responsePolicy.retentionPolicy === "keep_full" ? output : responsePolicy.summary;
2757
2753
  toolHistory.push({
2758
2754
  executionId,
2759
2755
  toolName,
2760
2756
  args,
2761
- output: storedOutput,
2757
+ output,
2762
2758
  rawOutput: output,
2763
2759
  timestamp,
2764
2760
  tool_call_id: tc.id,
2765
- summarized: responsePolicy.retentionPolicy !== "keep_full",
2761
+ summarized: false,
2766
2762
  originalTokenCount: responsePolicy.tokenCount,
2767
2763
  classification: responsePolicy.classification,
2768
- retentionPolicy: responsePolicy.retentionPolicy,
2769
- summary: responsePolicy.summary,
2770
- archiveId: responsePolicy.retentionPolicy === "summarize_archive" || responsePolicy.retentionPolicy === "drop" ? executionId : void 0,
2764
+ retentionPolicy: "keep_full",
2765
+ summary: void 0,
2766
+ archiveId: void 0,
2771
2767
  status: "success"
2772
2768
  });
2773
- if (resolved.context.archiveLargeToolResponses && (responsePolicy.retentionPolicy === "summarize_archive" || responsePolicy.retentionPolicy === "drop")) {
2774
- toolHistoryArchived.push({
2775
- executionId,
2776
- toolName,
2777
- args,
2778
- output,
2779
- rawOutput: output,
2780
- timestamp,
2781
- tool_call_id: tc.id,
2782
- summarized: true,
2783
- originalTokenCount: responsePolicy.tokenCount,
2784
- classification: responsePolicy.classification,
2785
- retentionPolicy: responsePolicy.retentionPolicy,
2786
- summary: responsePolicy.summary,
2787
- archiveId: executionId,
2788
- status: "success"
2789
- });
2790
- }
2791
- let toolMessageContent = responsePolicy.content;
2792
- if (responsePolicy.retentionPolicy === "summarize_archive") {
2793
- toolMessageContent = `ARCHIVED_TOOL_RESPONSE [executionId=${executionId}]: ${responsePolicy.summary}
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.`;
2795
- } else if (responsePolicy.retentionPolicy === "drop") {
2796
- toolMessageContent = `DROPPED_TOOL_RESPONSE [executionId=${executionId}]: ${responsePolicy.summary}
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.`;
2798
- }
2799
- appended.push({ role: "tool", content: toolMessageContent, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
2769
+ appended.push({ role: "tool", content: responsePolicy.content, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
2800
2770
  onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: output, durationMs });
2801
2771
  onProgress?.({ stage: "tools", message: `Tool ${tc.name} completed`, detail: { durationMs } });
2802
2772
  if (needsApproval && approvalEntry) {
@@ -2840,8 +2810,9 @@ Only use get_tool_response with executionId "${executionId}" if this summary is
2840
2810
  messageList,
2841
2811
  debug: {
2842
2812
  classification: responsePolicy.classification,
2843
- retentionPolicy: responsePolicy.retentionPolicy,
2844
- originalTokenCount: responsePolicy.tokenCount
2813
+ retentionPolicy: "keep_full",
2814
+ originalTokenCount: responsePolicy.tokenCount,
2815
+ truncated: responsePolicy.truncated
2845
2816
  }
2846
2817
  });
2847
2818
  toolCount += 1;
@@ -3980,40 +3951,6 @@ function createAgentNode(opts) {
3980
3951
  }
3981
3952
 
3982
3953
  // 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
- }
4017
3954
  function collectRecentTurns(messages, lastTurnsToKeep) {
4018
3955
  if (lastTurnsToKeep <= 0) return [];
4019
3956
  const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
@@ -4114,7 +4051,7 @@ function clampToBudget(messages, maxContextTokens) {
4114
4051
  function buildModelMessages(state, config) {
4115
4052
  const rawMessages = state.messages || [];
4116
4053
  if (config.context.policy === "raw") {
4117
- return clampToBudget(applyRecentFullOverride(rawMessages, state, config), config.limits.maxContextTokens);
4054
+ return clampToBudget(rawMessages, config.limits.maxContextTokens);
4118
4055
  }
4119
4056
  const recentMessages = collectRecentTurns(rawMessages, config.context.lastTurnsToKeep);
4120
4057
  const latestSummary = state.summaryRecords?.[state.summaryRecords.length - 1];
@@ -4134,7 +4071,7 @@ function buildModelMessages(state, config) {
4134
4071
  ...syntheticContextMessages,
4135
4072
  ...config.context.policy === "summary_only" ? body.filter((message) => message.role === "user" || message.role === "assistant").slice(-2) : body
4136
4073
  ];
4137
- return clampToBudget(applyRecentFullOverride(assembled, state, config), config.limits.maxContextTokens);
4074
+ return clampToBudget(assembled, config.limits.maxContextTokens);
4138
4075
  }
4139
4076
 
4140
4077
  // src/nodes/contextSummarize.ts
@@ -4236,24 +4173,8 @@ function extractCanonicalFacts(messages) {
4236
4173
  }
4237
4174
  return [...facts.values()];
4238
4175
  }
4239
- function flattenToolMessageContent(message) {
4240
- if (typeof message.content === "string") {
4241
- return message.content;
4242
- }
4243
- if (Array.isArray(message.content)) {
4244
- return message.content.map((part) => typeof part === "string" ? part : part?.text ?? part?.content ?? String(part ?? "")).join(" ");
4245
- }
4246
- return "";
4247
- }
4248
4176
  function isSummarizedToolPlaceholder(content) {
4249
- return typeof content === "string" && (content === "SUMMARIZED" || content.startsWith("SUMMARIZED_TOOL_RESPONSE"));
4250
- }
4251
- function summarizeToolMessagePreview(message) {
4252
- const preview = flattenToolMessageContent(message).trim();
4253
- if (!preview) {
4254
- return "No summary available.";
4255
- }
4256
- return preview.length > 240 ? `${preview.slice(0, 240)}...` : preview;
4177
+ return typeof content === "string" && (content === "SUMMARIZED" || content.startsWith("SUMMARIZED_TOOL_RESPONSE") || content.startsWith("ARCHIVED_TOOL_RESPONSE") || content.startsWith("STRUCTURED_TOOL_RESPONSE") || content.startsWith("DROPPED_TOOL_RESPONSE"));
4257
4178
  }
4258
4179
  function findToolHistoryEntry(state, toolCallId, toolName) {
4259
4180
  const entries = [
@@ -4279,29 +4200,6 @@ function findToolHistoryEntry(state, toolCallId, toolName) {
4279
4200
  }
4280
4201
  return void 0;
4281
4202
  }
4282
- function buildSummarizedToolMessage(message, state) {
4283
- const toolName = message.name || "unknown_tool";
4284
- const toolCallId = message.tool_call_id;
4285
- const historyEntry = findToolHistoryEntry(state, toolCallId, toolName);
4286
- const executionId = historyEntry?.executionId;
4287
- const summary = historyEntry?.summary || summarizeToolMessagePreview(message);
4288
- const references = [
4289
- `toolName=${toolName}`,
4290
- toolCallId ? `toolCallId=${toolCallId}` : null,
4291
- executionId ? `executionId=${executionId}` : null
4292
- ].filter(Boolean).join("; ");
4293
- let retrievalHint = "Only use get_tool_response if this summary is missing a specific field, id, snippet, or exact payload you still need.";
4294
- if (executionId && toolCallId) {
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.`;
4296
- } else if (executionId || toolCallId) {
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.`;
4298
- }
4299
- return [
4300
- `SUMMARIZED_TOOL_RESPONSE [${references}]`,
4301
- `Summary: ${summary}`,
4302
- retrievalHint
4303
- ].join("\n");
4304
- }
4305
4203
  function mergeStableFacts(summary, additionalFacts) {
4306
4204
  if (additionalFacts.length === 0) return summary;
4307
4205
  const merged = new Map(summary.stable_facts.map((fact) => [fact.key, fact]));
@@ -4359,20 +4257,13 @@ function createContextSummarizeNode(opts) {
4359
4257
  const messages = state.messages || [];
4360
4258
  const onEvent = state.ctx?.__onEvent;
4361
4259
  const traceSession = state.ctx?.__traceSession;
4362
- const toolResponsesConfigEarly = resolved.toolResponses;
4363
- const recentFullToolCallIdsEarly = collectRecentFullToolCallIds(state, toolResponsesConfigEarly.keepRecentFullCount);
4364
4260
  const compressableMessages = messages.filter(
4365
4261
  (m) => {
4366
4262
  if (m.role !== "tool") return false;
4367
4263
  if (isSummarizedToolPlaceholder(m.content)) return false;
4368
4264
  if (isSyntheticSummaryMessage(m)) return false;
4369
- if (m.tool_call_id && recentFullToolCallIdsEarly.has(m.tool_call_id)) return false;
4370
- const histEntry = findToolHistoryEntry(state, m.tool_call_id, m.name);
4371
- if (histEntry?.retentionPolicy === "keep_full") return false;
4372
- if (!histEntry?.retentionPolicy) {
4373
- const byTool = toolResponsesConfigEarly.toolResponseRetentionByTool?.[m.name || ""];
4374
- if ((byTool || toolResponsesConfigEarly.defaultPolicy) === "keep_full") return false;
4375
- }
4265
+ const policy = resolveSummarizationRetention(m.name || "", resolved);
4266
+ if (policy === "keep_full") return false;
4376
4267
  return true;
4377
4268
  }
4378
4269
  );
@@ -4552,8 +4443,8 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4552
4443
  break;
4553
4444
  }
4554
4445
  }
4555
- const toolResponsesConfig = resolved.toolResponses;
4556
- const recentFullToolCallIds = collectRecentFullToolCallIds(state, toolResponsesConfig.keepRecentFullCount);
4446
+ const archivedExecutions = [...state.toolHistoryArchived || []];
4447
+ const archivedIds = new Set(archivedExecutions.map((entry) => entry?.executionId).filter(Boolean));
4557
4448
  const newMessages = messages.map((m) => {
4558
4449
  if (m.role === "tool") {
4559
4450
  if (isSyntheticSummaryMessage(m)) {
@@ -4565,25 +4456,39 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4565
4456
  }
4566
4457
  if (isSummarizedToolPlaceholder(m.content)) return m;
4567
4458
  if (toolCallId && protectedToolCallIds.has(toolCallId)) return m;
4568
- if (toolCallId && recentFullToolCallIds.has(toolCallId)) return m;
4569
4459
  const toolName = m.name || "";
4460
+ const policy = resolveSummarizationRetention(toolName, resolved);
4461
+ if (policy === "keep_full") return m;
4570
4462
  const historyEntry = findToolHistoryEntry(state, toolCallId, toolName);
4571
- const entryRetention = historyEntry?.retentionPolicy;
4572
- if (entryRetention === "keep_full") return m;
4573
- if (!entryRetention) {
4574
- const byTool = toolResponsesConfig.toolResponseRetentionByTool?.[toolName];
4575
- const configPolicy = byTool || toolResponsesConfig.defaultPolicy;
4576
- if (configPolicy === "keep_full") return m;
4463
+ const rawOutput = historyEntry?.rawOutput ?? historyEntry?.output ?? m.content;
4464
+ const executionId = historyEntry?.executionId;
4465
+ const prebuiltSummary = historyEntry?.summary || summarizeObject(rawOutput);
4466
+ if ((policy === "summarize_archive" || policy === "drop") && historyEntry && executionId && !archivedIds.has(executionId)) {
4467
+ archivedExecutions.push({
4468
+ ...historyEntry,
4469
+ summarized: true,
4470
+ retentionPolicy: policy,
4471
+ archiveId: executionId,
4472
+ summary: prebuiltSummary
4473
+ });
4474
+ archivedIds.add(executionId);
4577
4475
  }
4578
4476
  return {
4579
4477
  ...m,
4580
- content: buildSummarizedToolMessage(m, state)
4478
+ content: renderRetainedToolMessage({
4479
+ policy,
4480
+ rawOutput,
4481
+ toolName,
4482
+ toolCallId,
4483
+ executionId,
4484
+ prebuiltSummary
4485
+ })
4581
4486
  };
4582
4487
  }
4583
4488
  return m;
4584
4489
  }).filter((m) => m !== null);
4585
4490
  const actuallyCompressed = newMessages.filter(
4586
- (m) => m.role === "tool" && typeof m.content === "string" && m.content.startsWith("SUMMARIZED_TOOL_RESPONSE") && !messages.some((orig) => orig.tool_call_id === m.tool_call_id && isSummarizedToolPlaceholder(orig.content))
4491
+ (m) => m.role === "tool" && isSummarizedToolPlaceholder(m.content) && !messages.some((orig) => orig.tool_call_id === m.tool_call_id && isSummarizedToolPlaceholder(orig.content))
4587
4492
  );
4588
4493
  if (actuallyCompressed.length === 0) {
4589
4494
  return {};
@@ -4662,7 +4567,8 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4662
4567
  return {
4663
4568
  messages: finalMessages,
4664
4569
  summaries,
4665
- summaryRecords
4570
+ summaryRecords,
4571
+ toolHistoryArchived: archivedExecutions
4666
4572
  };
4667
4573
  };
4668
4574
  }