@nopeek/agent-bridge 0.7.18 → 0.7.20
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/bot.js +10 -1
- package/dist/tool-progress.d.ts +3 -1
- package/dist/tool-progress.js +54 -10
- package/package.json +1 -1
package/dist/bot.js
CHANGED
|
@@ -613,7 +613,16 @@ export class BotRunner {
|
|
|
613
613
|
}
|
|
614
614
|
};
|
|
615
615
|
const published = new TurnPublisher({
|
|
616
|
-
stream: () =>
|
|
616
|
+
stream: async () => {
|
|
617
|
+
const s = await ch.stream();
|
|
618
|
+
return {
|
|
619
|
+
append: (chunk) => s.append(chunk),
|
|
620
|
+
replace: (full) => s.replace(full),
|
|
621
|
+
done: (final) => s.done(final),
|
|
622
|
+
fail: (text) => s.fail(text),
|
|
623
|
+
cancel: () => ch.recall(s.messageId),
|
|
624
|
+
};
|
|
625
|
+
},
|
|
617
626
|
send: async (body) => {
|
|
618
627
|
await ch.send({ text: body });
|
|
619
628
|
},
|
package/dist/tool-progress.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export interface StreamHandle {
|
|
|
15
15
|
replace(full: string): void;
|
|
16
16
|
done(finalText?: string): Promise<unknown>;
|
|
17
17
|
fail(text: string): Promise<void>;
|
|
18
|
+
/** Drop the placeholder bubble (NoPeek stream() starts as "…"). */
|
|
19
|
+
cancel(): Promise<void>;
|
|
18
20
|
}
|
|
19
21
|
export interface TurnSink {
|
|
20
22
|
stream(): Promise<StreamHandle>;
|
|
@@ -35,7 +37,7 @@ export type TurnPublisherOpts = {
|
|
|
35
37
|
export type TurnFinishKind = "streamed" | "sent" | "empty";
|
|
36
38
|
/** Text in `full` that has not already been posted as commentary. */
|
|
37
39
|
export declare function unpublishedTail(full: string, published: string): string;
|
|
38
|
-
/** Unified diffs /
|
|
40
|
+
/** Unified diffs / `┊ review diff` dumps should never land in the chat. */
|
|
39
41
|
export declare function isDumpLine(line: string): boolean;
|
|
40
42
|
/** True when the text has a user-readable recap, not just tools/dumps. */
|
|
41
43
|
export declare function isHumanBriefing(text: string): boolean;
|
package/dist/tool-progress.js
CHANGED
|
@@ -78,14 +78,18 @@ export function unpublishedTail(full, published) {
|
|
|
78
78
|
return f;
|
|
79
79
|
}
|
|
80
80
|
const DEFAULT_MAX_BUBBLE = 480;
|
|
81
|
-
/**
|
|
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
|
|
87
|
+
const l = bareLine(line);
|
|
84
88
|
if (!l)
|
|
85
89
|
return false;
|
|
86
|
-
if (/^review
|
|
90
|
+
if (/^review\s+diff\b/i.test(l))
|
|
87
91
|
return true;
|
|
88
|
-
if (
|
|
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) || /^(@@ |[+-](?![+-])
|
|
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();
|
|
@@ -412,7 +445,13 @@ export class TurnPublisher {
|
|
|
412
445
|
s.append(extra);
|
|
413
446
|
const parts = splitBubbles(final, this.maxBubbleChars);
|
|
414
447
|
if (!parts.length) {
|
|
415
|
-
|
|
448
|
+
const keep = stripDumps(final).trim();
|
|
449
|
+
if (keep && isHumanBriefing(keep)) {
|
|
450
|
+
await s.done(keep).catch(() => { });
|
|
451
|
+
this.published += keep;
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
await s.cancel().catch(() => { });
|
|
416
455
|
return;
|
|
417
456
|
}
|
|
418
457
|
const first = parts[0];
|
|
@@ -438,8 +477,9 @@ export class TurnPublisher {
|
|
|
438
477
|
}
|
|
439
478
|
/** Final recap stays one Telegram-style bubble. Dumps still dropped. */
|
|
440
479
|
async sendBriefing(text) {
|
|
441
|
-
const
|
|
442
|
-
|
|
480
|
+
const parts = splitBubbles(text.replace(/\r\n/g, "\n"), 4000);
|
|
481
|
+
const cleaned = parts.join("\n\n").trim();
|
|
482
|
+
if (!cleaned || isToolDump(cleaned) || !isHumanBriefing(cleaned))
|
|
443
483
|
return false;
|
|
444
484
|
await this.sink.send(cleaned);
|
|
445
485
|
this.published += this.published ? `\n\n${cleaned}` : cleaned;
|
|
@@ -489,7 +529,11 @@ export class TurnPublisher {
|
|
|
489
529
|
this.progress = null;
|
|
490
530
|
const text = this.progressLines.join("\n");
|
|
491
531
|
this.progressLines = [];
|
|
492
|
-
if (s
|
|
532
|
+
if (!s)
|
|
533
|
+
return;
|
|
534
|
+
if (text)
|
|
493
535
|
await s.done(text).catch(() => { });
|
|
536
|
+
else
|
|
537
|
+
await s.cancel().catch(() => { });
|
|
494
538
|
}
|
|
495
539
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nopeek/agent-bridge",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.20",
|
|
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",
|