@nopeek/agent-bridge 0.7.18 → 0.7.19

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.
@@ -35,7 +35,7 @@ export type TurnPublisherOpts = {
35
35
  export type TurnFinishKind = "streamed" | "sent" | "empty";
36
36
  /** Text in `full` that has not already been posted as commentary. */
37
37
  export declare function unpublishedTail(full: string, published: string): string;
38
- /** Unified diffs / `| review` dumps should never land in the chat. */
38
+ /** Unified diffs / `┊ review diff` dumps should never land in the chat. */
39
39
  export declare function isDumpLine(line: string): boolean;
40
40
  /** True when the text has a user-readable recap, not just tools/dumps. */
41
41
  export declare function isHumanBriefing(text: string): boolean;
@@ -78,14 +78,18 @@ export function unpublishedTail(full, published) {
78
78
  return f;
79
79
  }
80
80
  const DEFAULT_MAX_BUBBLE = 480;
81
- /** Unified diffs / `| review` dumps should never land in the chat. */
81
+ /** Hermes TUI activity prefix: review diff / │ read_file / | patch */
82
+ function bareLine(line) {
83
+ return line.replace(/^[┊│|]\s*/, "").trim();
84
+ }
85
+ /** Unified diffs / `┊ review diff` dumps should never land in the chat. */
82
86
  export function isDumpLine(line) {
83
- const l = line.trim();
87
+ const l = bareLine(line);
84
88
  if (!l)
85
89
  return false;
86
- if (/^review diff\b/i.test(l))
90
+ if (/^review\s+diff\b/i.test(l))
87
91
  return true;
88
- if (/^\|\s*(review|read|search|terminal|patch|write)\b/i.test(l))
92
+ if (/^(review|read|search|terminal|patch|write)\b/i.test(l) && line.trim().startsWith("|"))
89
93
  return true;
90
94
  if (/^@@\s+-/.test(l))
91
95
  return true;
@@ -97,6 +101,9 @@ export function isDumpLine(line) {
97
101
  return true;
98
102
  if (/^index [0-9a-f]+\.\.[0-9a-f]+/.test(l))
99
103
  return true;
104
+ // Diff of a markdown list item becomes "-- foo" / "+- foo".
105
+ if (/^[+-]{2}\s+\S/.test(l))
106
+ return true;
100
107
  // Unified-diff / review lines: "+ const x =" not "+1 country".
101
108
  if (/^[+-]\s{2,}\S/.test(l))
102
109
  return true;
@@ -116,6 +123,8 @@ function isToolProgressLine(line) {
116
123
  }
117
124
  /** True when the text has a user-readable recap, not just tools/dumps. */
118
125
  export function isHumanBriefing(text) {
126
+ if (isToolDump(text))
127
+ return false;
119
128
  const prose = text
120
129
  .split("\n")
121
130
  .filter((l) => {
@@ -132,13 +141,19 @@ export function isToolDump(text) {
132
141
  const t = text.trim();
133
142
  if (!t)
134
143
  return false;
144
+ if (/review\s+diff/i.test(t))
145
+ return true;
135
146
  if (isDumpLine(t))
136
147
  return true;
137
148
  if (/^diff --git /m.test(t) && /^@@ /m.test(t))
138
149
  return true;
150
+ if (/^--\s+\S/m.test(t) && /^\+-\s+\S/m.test(t))
151
+ return true;
152
+ if (/^\s*#{1,3}\s+Voice\b/m.test(t) && /^\s*#{1,3}\s+Memory\b/m.test(t))
153
+ return true;
139
154
  const lines = t.split("\n");
140
155
  if (lines.length >= 6) {
141
- const dumpy = lines.filter((l) => isDumpLine(l) || /^(@@ |[+-](?![+-])|\| )/.test(l)).length;
156
+ const dumpy = lines.filter((l) => isDumpLine(l) || /^(@@ |[+-](?![+-])|[┊│|]\s)/.test(l)).length;
142
157
  if (dumpy / lines.length >= 0.4)
143
158
  return true;
144
159
  }
@@ -162,9 +177,15 @@ export function splitBubbles(text, max = DEFAULT_MAX_BUBBLE) {
162
177
  return [];
163
178
  const paras = cleaned.split(/\n{2,}/).map((p) => p.trim()).filter(Boolean);
164
179
  const out = [];
180
+ let afterDump = false;
165
181
  for (const p of paras) {
166
- if (isToolDump(p))
182
+ if (isToolDump(p) || /review\s+diff/i.test(p)) {
183
+ afterDump = true;
167
184
  continue;
185
+ }
186
+ if (afterDump && !looksLikeCloseout(p))
187
+ continue;
188
+ afterDump = false;
168
189
  if (p.length <= max) {
169
190
  out.push(p);
170
191
  continue;
@@ -194,6 +215,18 @@ export function splitBubbles(text, max = DEFAULT_MAX_BUBBLE) {
194
215
  }
195
216
  return out;
196
217
  }
218
+ /** After a dump hunk, only keep an actual close-out — not leftover SOUL/skill text. */
219
+ function looksLikeCloseout(p) {
220
+ if (/what was wrong|what i did|current state|^\*\*What /im.test(p))
221
+ return true;
222
+ if (/^Done\b/i.test(p.trim()))
223
+ return true;
224
+ if (/^\s*#{1,3}\s/m.test(p))
225
+ return false;
226
+ if (isToolDump(p))
227
+ return false;
228
+ return isHumanBriefing(p);
229
+ }
197
230
  function hardWrap(s, max) {
198
231
  const out = [];
199
232
  let rest = s.trim();
@@ -438,8 +471,9 @@ export class TurnPublisher {
438
471
  }
439
472
  /** Final recap stays one Telegram-style bubble. Dumps still dropped. */
440
473
  async sendBriefing(text) {
441
- const cleaned = stripDumps(text.replace(/\r\n/g, "\n")).trim();
442
- if (!cleaned || isToolDump(cleaned))
474
+ const parts = splitBubbles(text.replace(/\r\n/g, "\n"), 4000);
475
+ const cleaned = parts.join("\n\n").trim();
476
+ if (!cleaned || isToolDump(cleaned) || !isHumanBriefing(cleaned))
443
477
  return false;
444
478
  await this.sink.send(cleaned);
445
479
  this.published += this.published ? `\n\n${cleaned}` : cleaned;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nopeek/agent-bridge",
3
- "version": "0.7.18",
3
+ "version": "0.7.19",
4
4
  "description": "Run your own agents as E2EE NoPeek bots. Pairs with one-time codes (multiple accounts per computer), runs every bot each account owns, and pipes messages to any command or webhook.",
5
5
  "type": "module",
6
6
  "license": "MIT",