@caupulican/pi-adaptative 0.80.19 → 0.80.21

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.
@@ -34,6 +34,78 @@ import { buildSystemPrompt } from "./system-prompt.js";
34
34
  import { createLocalBashOperations } from "./tools/bash.js";
35
35
  import { createAllToolDefinitions } from "./tools/index.js";
36
36
  import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
37
+ const MAX_RETAINED_TOOL_RESULT_DETAILS_BYTES = 32 * 1024;
38
+ function estimateJsonLikeBytes(value, maxBytes) {
39
+ const seen = new WeakSet();
40
+ let bytes = 0;
41
+ const add = (amount) => {
42
+ bytes += amount;
43
+ return bytes <= maxBytes;
44
+ };
45
+ const visit = (current) => {
46
+ if (bytes > maxBytes)
47
+ return false;
48
+ if (current === null)
49
+ return add(4);
50
+ if (current === undefined)
51
+ return add(9);
52
+ if (typeof current === "string")
53
+ return add(current.length * 4 + 2);
54
+ if (typeof current === "number")
55
+ return add(24);
56
+ if (typeof current === "boolean")
57
+ return add(current ? 4 : 5);
58
+ if (typeof current === "bigint")
59
+ return add(current.toString().length + 2);
60
+ if (typeof current === "symbol" || typeof current === "function")
61
+ return add(12);
62
+ if (typeof current !== "object")
63
+ return add(8);
64
+ const objectValue = current;
65
+ if (seen.has(objectValue))
66
+ return add(20);
67
+ seen.add(objectValue);
68
+ if (Array.isArray(objectValue)) {
69
+ if (!add(2))
70
+ return false;
71
+ for (let index = 0; index < objectValue.length; index++) {
72
+ if (!add(index === 0 ? 0 : 1))
73
+ return false;
74
+ if (!visit(objectValue[index]))
75
+ return false;
76
+ }
77
+ return bytes <= maxBytes;
78
+ }
79
+ if (!add(2))
80
+ return false;
81
+ let first = true;
82
+ for (const key in objectValue) {
83
+ if (!Object.hasOwn(objectValue, key))
84
+ continue;
85
+ if (!add((first ? 0 : 1) + key.length * 4 + 3))
86
+ return false;
87
+ first = false;
88
+ if (!visit(objectValue[key]))
89
+ return false;
90
+ }
91
+ return bytes <= maxBytes;
92
+ };
93
+ visit(value);
94
+ return { bytes, exceeded: bytes > maxBytes };
95
+ }
96
+ function compactToolResultDetailsForRetention(message) {
97
+ if (message.role !== "toolResult" || message.details === undefined)
98
+ return;
99
+ const estimate = estimateJsonLikeBytes(message.details, MAX_RETAINED_TOOL_RESULT_DETAILS_BYTES);
100
+ if (!estimate.exceeded)
101
+ return;
102
+ message.details = {
103
+ piToolResultDetailsTruncated: true,
104
+ reason: "Tool result details exceeded retention budget; model-visible content was retained.",
105
+ minimumBytes: estimate.bytes,
106
+ maxRetainedBytes: MAX_RETAINED_TOOL_RESULT_DETAILS_BYTES,
107
+ };
108
+ }
37
109
  /**
38
110
  * Parse a skill block from message text.
39
111
  * Returns null if the text doesn't contain a skill block.
@@ -344,8 +416,11 @@ export class AgentSession {
344
416
  await this._emitExtensionEvent(event);
345
417
  // Notify all listeners
346
418
  this._emit(event.type === "agent_end" ? { ...event, willRetry: this._willRetryAfterAgentEnd(event) } : event);
347
- // Handle session persistence
419
+ // Handle session/context retention. Tool result details are UI/log metadata,
420
+ // not provider-visible content, and large graph/search payloads can otherwise
421
+ // accumulate until the interactive Node process hits the V8 heap limit.
348
422
  if (event.type === "message_end") {
423
+ compactToolResultDetailsForRetention(event.message);
349
424
  // Check if this is a custom message from extensions
350
425
  if (event.message.role === "custom") {
351
426
  // Persist as CustomMessageEntry