@pentoshi/clai 3.13.0 → 3.13.1

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 pentoshi007
3
+ Copyright (c) 2026 pentoshi007
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,24 +1,12 @@
1
- /**
2
- * Cap in-memory / re-sent message bulk from native toolCalls.
3
- *
4
- * fs.write / writeMany put entire file bodies in tool-call args. Those were
5
- * stored verbatim in history and LoopGuard signatures, so a scaffold turn
6
- * could hold many multi-MB strings — memory climbed into the multi-GB range
7
- * and Macs heated under GC. Full file bodies remain on disk; tool results
8
- * still report paths/bytes.
9
- */
10
- /** Strings at or above this are replaced with a length+hash stub. */
11
1
  export declare const SLIM_ARG_STRING_CHARS = 400;
12
- /** Hard ceiling for a single string kept as-is in history (safety). */
13
2
  export declare const SLIM_ARG_ABSOLUTE_MAX_CHARS = 8000;
14
- /**
15
- * Replace large string values with a compact stub so history/loop-guard
16
- * never retains full write payloads. Small strings and structure stay intact
17
- * so path-based identity still works for loop detection.
18
- */
19
- export declare function slimValue(value: unknown, depth?: number): unknown;
3
+ export declare function slimValue(value: unknown, depth?: number, key?: string): unknown;
20
4
  export declare function slimToolArgs(args: Record<string, unknown>): Record<string, unknown>;
21
- /** Approximate UTF-16 code units for token budgeting (same basis as content). */
5
+ export declare function findElidedStubArg(value: unknown, path?: string, depth?: number): {
6
+ key: string;
7
+ value: string;
8
+ } | undefined;
9
+ export declare function elidedStubReuseMessage(key: string): string;
22
10
  export declare function measureToolCallsChars(toolCalls: readonly {
23
11
  readonly name?: string;
24
12
  readonly id?: string;
@@ -1,33 +1,23 @@
1
- /**
2
- * Cap in-memory / re-sent message bulk from native toolCalls.
3
- *
4
- * fs.write / writeMany put entire file bodies in tool-call args. Those were
5
- * stored verbatim in history and LoopGuard signatures, so a scaffold turn
6
- * could hold many multi-MB strings — memory climbed into the multi-GB range
7
- * and Macs heated under GC. Full file bodies remain on disk; tool results
8
- * still report paths/bytes.
9
- */
10
1
  import { createHash } from "node:crypto";
11
- /** Strings at or above this are replaced with a length+hash stub. */
12
2
  export const SLIM_ARG_STRING_CHARS = 400;
13
- /** Hard ceiling for a single string kept as-is in history (safety). */
14
3
  export const SLIM_ARG_ABSOLUTE_MAX_CHARS = 8_000;
15
- /** Max depth when walking args trees (writeMany files[]). */
16
4
  const SLIM_MAX_DEPTH = 6;
5
+ const BULK_ARG_KEYS = new Set(["content", "body"]);
6
+ const ELIDED_STUB_PATTERN = /^«\d+ chars sha256=[0-9a-f]{12}(?:\s+—.*)?»$/s;
17
7
  function shortHash(text) {
18
8
  return createHash("sha256").update(text).digest("hex").slice(0, 12);
19
9
  }
20
- /**
21
- * Replace large string values with a compact stub so history/loop-guard
22
- * never retains full write payloads. Small strings and structure stay intact
23
- * so path-based identity still works for loop detection.
24
- */
25
- export function slimValue(value, depth = 0) {
10
+ function slimLimitForKey(key) {
11
+ return key !== undefined && BULK_ARG_KEYS.has(key)
12
+ ? SLIM_ARG_STRING_CHARS
13
+ : SLIM_ARG_ABSOLUTE_MAX_CHARS;
14
+ }
15
+ export function slimValue(value, depth = 0, key) {
26
16
  if (typeof value === "string") {
27
- if (value.length < SLIM_ARG_STRING_CHARS)
17
+ if (value.length < slimLimitForKey(key))
28
18
  return value;
29
19
  const hash = shortHash(value);
30
- return `«${value.length} chars sha256=${hash}»`;
20
+ return `«${value.length} chars sha256=${hash} — elided from history; never reuse this stub, regenerate the full value»`;
31
21
  }
32
22
  if (value === null || value === undefined)
33
23
  return value;
@@ -42,8 +32,8 @@ export function slimValue(value, depth = 0) {
42
32
  return value.map((entry) => slimValue(entry, depth + 1));
43
33
  }
44
34
  const out = {};
45
- for (const key of Object.keys(value).sort()) {
46
- out[key] = slimValue(value[key], depth + 1);
35
+ for (const childKey of Object.keys(value).sort()) {
36
+ out[childKey] = slimValue(value[childKey], depth + 1, childKey);
47
37
  }
48
38
  return out;
49
39
  }
@@ -54,7 +44,35 @@ export function slimToolArgs(args) {
54
44
  }
55
45
  return {};
56
46
  }
57
- /** Approximate UTF-16 code units for token budgeting (same basis as content). */
47
+ export function findElidedStubArg(value, path = "args", depth = 0) {
48
+ if (typeof value === "string") {
49
+ return ELIDED_STUB_PATTERN.test(value) ? { key: path, value } : undefined;
50
+ }
51
+ if (value === null || value === undefined || typeof value !== "object") {
52
+ return undefined;
53
+ }
54
+ if (depth >= SLIM_MAX_DEPTH)
55
+ return undefined;
56
+ if (Array.isArray(value)) {
57
+ for (let i = 0; i < value.length; i += 1) {
58
+ const found = findElidedStubArg(value[i], `${path}[${i}]`, depth + 1);
59
+ if (found)
60
+ return found;
61
+ }
62
+ return undefined;
63
+ }
64
+ for (const [childKey, childValue] of Object.entries(value)) {
65
+ const found = findElidedStubArg(childValue, path ? `${path}.${childKey}` : childKey, depth + 1);
66
+ if (found)
67
+ return found;
68
+ }
69
+ return undefined;
70
+ }
71
+ export function elidedStubReuseMessage(key) {
72
+ return (`Tool call rejected: argument "${key}" is an elided history placeholder («N chars sha256=…»), not a real value. ` +
73
+ "Compressed history replaces long arguments with those stubs and the original text cannot be recovered from them. " +
74
+ "Re-issue the tool call with the complete literal value — never copy «…» stubs from earlier context.");
75
+ }
58
76
  export function measureToolCallsChars(toolCalls) {
59
77
  if (!toolCalls?.length)
60
78
  return 0;
@@ -1 +1 @@
1
- {"version":3,"file":"message-slim.js","sourceRoot":"","sources":["../../src/agent/message-slim.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"message-slim.js","sourceRoot":"","sources":["../../src/agent/message-slim.ts"],"names":[],"mappings":"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"}
@@ -39,6 +39,7 @@ function safeEngagementActionsForToolCall(call) {
39
39
  }
40
40
  import { availableToolNames, normalizeToolCall, runToolCall, BATCH_SAFE_TOOLS, } from "../tools/registry.js";
41
41
  import { getToolDefinitions, getCompactToolDefinitions, RUNNER_META_TOOL_NAMES, } from "../tools/definitions.js";
42
+ import { elidedStubReuseMessage, findElidedStubArg, } from "./message-slim.js";
42
43
  import { appendAssistantWithTools, ensureUniqueToolCallIds, toolCallIdsInHistory, appendToolResult, assertValidToolProtocol, fillMissingToolResults, repairToolProtocol, } from "./tool-history.js";
43
44
  import { formatViewportHint, registerViewport } from "../ui/output-pane.js";
44
45
  import { compactMessagesWithSummary, shouldApplyAutoCompact, COMPACTION_MEMORY_PREFIX, PLAN_IMPLEMENT_MEMORY_PREFIX, isCompactionMemoryMessage, } from "./context-manager.js";
@@ -1265,6 +1266,13 @@ export async function runAgentTurn(prompt, options = {}) {
1265
1266
  emitToolResult(toolEventId, result, reason);
1266
1267
  return { ok: false, call, result, contextOutput: reason };
1267
1268
  }
1269
+ const elidedStub = findElidedStubArg(call.args);
1270
+ if (elidedStub) {
1271
+ const reason = elidedStubReuseMessage(elidedStub.key);
1272
+ const result = { ok: false, output: reason, exitCode: 1 };
1273
+ emitToolResult(toolEventId, result, reason);
1274
+ return { ok: false, call, result, contextOutput: reason };
1275
+ }
1268
1276
  if (call.name === "image.ocr" && !imageOcrEnabled) {
1269
1277
  writeNotice("info", "skipped OCR because the original image is attached to the vision model", chalk.dim(" ℹ skipped OCR — inspecting the attached image directly\n"));
1270
1278
  const recoveryText = "The original image is attached to this message and you can inspect it directly. " +