codebot-ai 1.4.2 → 1.4.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/cli.js CHANGED
@@ -44,7 +44,7 @@ const setup_1 = require("./setup");
44
44
  const banner_1 = require("./banner");
45
45
  const tools_1 = require("./tools");
46
46
  const scheduler_1 = require("./scheduler");
47
- const VERSION = '1.4.2';
47
+ const VERSION = '1.4.3';
48
48
  // Session-wide token tracking
49
49
  let sessionTokens = { input: 0, output: 0, total: 0 };
50
50
  const C = {
@@ -14,9 +14,16 @@ export declare class ContextManager {
14
14
  availableTokens(): number;
15
15
  /** Check if messages fit within budget */
16
16
  fitsInBudget(messages: Message[]): boolean;
17
- /** Compact conversation by dropping old messages and inserting a summary placeholder */
17
+ /**
18
+ * Group messages into atomic blocks that must never be split.
19
+ * An assistant message with tool_calls + its following tool responses = one block.
20
+ * All other messages are individual blocks.
21
+ * This prevents compaction from creating orphaned tool messages.
22
+ */
23
+ private groupMessages;
24
+ /** Compact conversation by dropping old messages. Never splits tool_call groups. */
18
25
  compact(messages: Message[], force?: boolean): Message[];
19
- /** Smart compaction: use LLM to summarize dropped messages instead of just discarding */
26
+ /** Smart compaction: use LLM to summarize dropped messages. Never splits tool_call groups. */
20
27
  compactWithSummary(messages: Message[]): Promise<{
21
28
  messages: Message[];
22
29
  summary: string;
@@ -29,23 +29,55 @@ class ContextManager {
29
29
  const total = messages.reduce((sum, m) => sum + this.estimateTokens(m.content), 0);
30
30
  return total <= this.availableTokens();
31
31
  }
32
- /** Compact conversation by dropping old messages and inserting a summary placeholder */
32
+ /**
33
+ * Group messages into atomic blocks that must never be split.
34
+ * An assistant message with tool_calls + its following tool responses = one block.
35
+ * All other messages are individual blocks.
36
+ * This prevents compaction from creating orphaned tool messages.
37
+ */
38
+ groupMessages(messages) {
39
+ const groups = [];
40
+ let i = 0;
41
+ while (i < messages.length) {
42
+ const msg = messages[i];
43
+ if (msg.role === 'assistant' && msg.tool_calls?.length) {
44
+ // Start of a tool_call group — keep assistant + all following tool messages together
45
+ const group = [msg];
46
+ i++;
47
+ while (i < messages.length && messages[i].role === 'tool') {
48
+ group.push(messages[i]);
49
+ i++;
50
+ }
51
+ groups.push(group);
52
+ }
53
+ else {
54
+ groups.push([msg]);
55
+ i++;
56
+ }
57
+ }
58
+ return groups;
59
+ }
60
+ /** Compact conversation by dropping old messages. Never splits tool_call groups. */
33
61
  compact(messages, force = false) {
34
62
  if (!force && this.fitsInBudget(messages))
35
63
  return messages;
36
64
  const system = messages[0]?.role === 'system' ? messages[0] : null;
37
65
  const rest = system ? messages.slice(1) : [...messages];
38
- // Keep recent messages that fit within 80% of budget
39
- const kept = [];
66
+ // Group messages into atomic blocks (assistant + tool responses stay together)
67
+ const groups = this.groupMessages(rest);
68
+ // Keep recent groups that fit within 80% of budget
69
+ const keptGroups = [];
40
70
  let tokenCount = 0;
41
71
  const budget = this.availableTokens();
42
- for (let i = rest.length - 1; i >= 0; i--) {
43
- const msgTokens = this.estimateTokens(rest[i].content);
44
- if (tokenCount + msgTokens > budget * 0.8)
72
+ for (let i = groups.length - 1; i >= 0; i--) {
73
+ const group = groups[i];
74
+ const groupTokens = group.reduce((sum, m) => sum + this.estimateTokens(m.content), 0);
75
+ if (tokenCount + groupTokens > budget * 0.8)
45
76
  break;
46
- kept.unshift(rest[i]);
47
- tokenCount += msgTokens;
77
+ keptGroups.unshift(group);
78
+ tokenCount += groupTokens;
48
79
  }
80
+ const kept = keptGroups.flat();
49
81
  const dropped = rest.length - kept.length;
50
82
  if (dropped > 0) {
51
83
  kept.unshift({
@@ -57,21 +89,25 @@ class ContextManager {
57
89
  kept.unshift(system);
58
90
  return kept;
59
91
  }
60
- /** Smart compaction: use LLM to summarize dropped messages instead of just discarding */
92
+ /** Smart compaction: use LLM to summarize dropped messages. Never splits tool_call groups. */
61
93
  async compactWithSummary(messages) {
62
94
  const system = messages[0]?.role === 'system' ? messages[0] : null;
63
95
  const rest = system ? messages.slice(1) : [...messages];
64
- // Determine which messages to keep vs summarize
65
- const kept = [];
96
+ // Group messages into atomic blocks
97
+ const groups = this.groupMessages(rest);
98
+ // Keep recent groups that fit within 80% of budget
99
+ const keptGroups = [];
66
100
  let tokenCount = 0;
67
101
  const budget = this.availableTokens();
68
- for (let i = rest.length - 1; i >= 0; i--) {
69
- const msgTokens = this.estimateTokens(rest[i].content);
70
- if (tokenCount + msgTokens > budget * 0.8)
102
+ for (let i = groups.length - 1; i >= 0; i--) {
103
+ const group = groups[i];
104
+ const groupTokens = group.reduce((sum, m) => sum + this.estimateTokens(m.content), 0);
105
+ if (tokenCount + groupTokens > budget * 0.8)
71
106
  break;
72
- kept.unshift(rest[i]);
73
- tokenCount += msgTokens;
107
+ keptGroups.unshift(group);
108
+ tokenCount += groupTokens;
74
109
  }
110
+ const kept = keptGroups.flat();
75
111
  const droppedCount = rest.length - kept.length;
76
112
  if (droppedCount === 0) {
77
113
  return { messages, summary: '' };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebot-ai",
3
- "version": "1.4.2",
3
+ "version": "1.4.3",
4
4
  "description": "Zero-dependency autonomous AI agent. Code, browse, search, automate. Works with any LLM — Ollama, Claude, GPT, Gemini, DeepSeek, Groq, Mistral, Grok.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",