@tritard/waterbrother 0.14.10 → 0.14.11

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/agent.js +32 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tritard/waterbrother",
3
- "version": "0.14.10",
3
+ "version": "0.14.11",
4
4
  "description": "Waterbrother: Grok-powered coding CLI with local tools, sessions, operator modes, and approval controls",
5
5
  "type": "module",
6
6
  "bin": {
package/src/agent.js CHANGED
@@ -76,7 +76,8 @@ When you use tools:
76
76
  - Never claim you ran commands you did not run.
77
77
  - If a tool fails, show the failure and recover.
78
78
  - You are primarily a coding assistant, but you can also have normal conversations. When the user wants to chat, just chat — be natural, helpful, and engaging. Do not constantly redirect to coding or push "what do you want to build?" when the user is clearly just talking.
79
- - Only use tools (file writes, shell commands) when the user asks for actual code work. Do not create files for joke requests, hypothetical questions, or non-engineering prompts.`;
79
+ - Only use tools (file writes, shell commands) when the user asks for actual code work. Do not create files for joke requests, hypothetical questions, or non-engineering prompts.
80
+ - You can call multiple tools in a single response. When multiple independent pieces of information are needed, make all calls in parallel for optimal performance. For example, if you need to read 3 files, read all 3 in one response rather than one at a time.`;
80
81
 
81
82
  const COMPACTION_SYSTEM_PROMPT = `You are compacting a coding assistant conversation for context resumption. Write a continuation summary that allows efficient resumption in a new context window where the conversation history will be replaced with this summary.
82
83
 
@@ -126,6 +127,7 @@ const PROFILE_PROMPTS = {
126
127
  const MAX_TOOL_CALL_ITERATIONS = 24;
127
128
  const LOCAL_COMPACTION_TRANSCRIPT_CHARS = 400_000;
128
129
  const LOCAL_COMPACTION_MESSAGE_PREVIEW_CHARS = 280;
130
+ const MAX_COMPACTION_CONSECUTIVE_FAILURES = 3;
129
131
 
130
132
  function buildSystemPrompt(profile, experienceMode = "standard", autonomyMode = "scoped", memory = "", executionContext = null) {
131
133
  const profileLine = PROFILE_PROMPTS[profile] || PROFILE_PROMPTS.coder;
@@ -321,6 +323,8 @@ export class Agent {
321
323
  this.autonomyMode = normalizeAutonomyMode(autonomyMode);
322
324
  this.requireTurnContracts = requireTurnContracts !== false;
323
325
  this.executionContext = null;
326
+ this._compactionFailures = 0;
327
+ this._transcriptPath = null;
324
328
 
325
329
  this.messages = [{ role: "system", content: buildSystemPrompt(profile, this.experienceMode, this.autonomyMode, this.memory) }];
326
330
  this.toolRuntime = createToolRuntime({
@@ -442,6 +446,11 @@ export class Agent {
442
446
  }
443
447
 
444
448
  async compactConversation({ keepLast = 24, signal } = {}) {
449
+ // Circuit breaker: stop trying after consecutive failures
450
+ if (this._compactionFailures >= MAX_COMPACTION_CONSECUTIVE_FAILURES) {
451
+ return { compacted: false, reason: "circuit_breaker", totalMessages: this.getSessionMessages().length };
452
+ }
453
+
445
454
  const keep = Math.max(8, Math.floor(Number(keepLast) || 24));
446
455
  const sessionMessages = this.getSessionMessages();
447
456
  if (sessionMessages.length <= keep + 2) {
@@ -486,14 +495,35 @@ export class Agent {
486
495
  usage = completion?.usage || null;
487
496
  } catch (error) {
488
497
  if (isAbortError(error)) throw error;
498
+ this._compactionFailures++;
489
499
  summaryBody = summarizeMessagesLocally(toSummarize);
490
500
  method = "local-fallback";
491
501
  }
492
502
  }
493
503
 
504
+ // Reset circuit breaker on success
505
+ this._compactionFailures = 0;
506
+
507
+ // Save transcript to disk before compacting — allows recovery of details
508
+ let transcriptNote = "";
509
+ try {
510
+ if (this.cwd) {
511
+ const { join } = await import("node:path");
512
+ const { mkdir, appendFile } = await import("node:fs/promises");
513
+ const dir = join(this.cwd, ".waterbrother");
514
+ await mkdir(dir, { recursive: true });
515
+ const transcriptPath = this._transcriptPath || join(dir, `transcript-${Date.now()}.jsonl`);
516
+ this._transcriptPath = transcriptPath;
517
+ for (const msg of toSummarize) {
518
+ await appendFile(transcriptPath, JSON.stringify(msg) + "\n", "utf8");
519
+ }
520
+ transcriptNote = `\n\nIf you need specific details from before compaction (like exact code snippets, error messages, or content you generated), read the full transcript at: ${transcriptPath}`;
521
+ }
522
+ } catch {}
523
+
494
524
  const summaryMessage = {
495
525
  role: "assistant",
496
- content: `### Compacted context summary\n${summaryBody}`
526
+ content: `### Compacted context summary\n${summaryBody}${transcriptNote}`
497
527
  };
498
528
 
499
529
  this.messages = [