@nopeek/agent-bridge 0.7.19 → 0.7.21
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/backends.js +4 -0
- package/dist/bot.js +16 -3
- package/dist/bridge.d.ts +1 -1
- package/dist/bridge.js +1 -1
- package/dist/inbound-files.js +1 -1
- package/dist/mid-turn.d.ts +15 -0
- package/dist/mid-turn.js +21 -0
- package/dist/tool-progress.d.ts +2 -0
- package/dist/tool-progress.js +12 -2
- package/package.json +1 -1
package/dist/backends.js
CHANGED
|
@@ -620,6 +620,10 @@ export function hermesBrain(cfg) {
|
|
|
620
620
|
const reply = await http(text, ctx, hooks);
|
|
621
621
|
if (reply)
|
|
622
622
|
return reply;
|
|
623
|
+
// A mid-turn abort is intentional (user follow-up). Do not spawn a
|
|
624
|
+
// second Hermes CLI job — that is how empty "…" bubbles stacked up.
|
|
625
|
+
if (ctx.signal?.aborted)
|
|
626
|
+
return "";
|
|
623
627
|
console.error(`[brain:hermes:@${ctx.botHandle.replace(/^@/, "")}] API returned empty — falling back to CLI`);
|
|
624
628
|
}
|
|
625
629
|
return cli(text, ctx, hooks);
|
package/dist/bot.js
CHANGED
|
@@ -8,7 +8,7 @@ import { FALLBACK_REPLY, resolveBrain } from "./brain.js";
|
|
|
8
8
|
import { beginTurn, finishTurn, lastTurnFor } from "./last-turn.js";
|
|
9
9
|
import { FileStore } from "./storage.js";
|
|
10
10
|
import { TurnPublisher } from "./tool-progress.js";
|
|
11
|
-
import { ChannelTurn, coalesceFollowups, isStopRequest, wrapInterruptedFollowup, } from "./mid-turn.js";
|
|
11
|
+
import { ChannelTurn, coalesceFollowups, isBotEcho, isStopRequest, wrapInterruptedFollowup, } from "./mid-turn.js";
|
|
12
12
|
import { buildInboundPrompt, describeStructured, hasInboundWork, isControlType, parseAttachments, saveInboundFiles, } from "./inbound-files.js";
|
|
13
13
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
14
14
|
const MAX_BACKOFF_MS = 60_000;
|
|
@@ -378,6 +378,10 @@ export class BotRunner {
|
|
|
378
378
|
np.on("member.joined", onRosterChange);
|
|
379
379
|
np.on("member.left", onRosterChange);
|
|
380
380
|
np.on("message", ((m) => {
|
|
381
|
+
// Own stream() placeholders echo back as message.new. Never interrupt
|
|
382
|
+
// or answer those — that loop is what left "…" edited bubbles.
|
|
383
|
+
if (isBotEcho(m, this.info.userId))
|
|
384
|
+
return;
|
|
381
385
|
const live = this.turns.get(m.channelId);
|
|
382
386
|
if (live?.active) {
|
|
383
387
|
live.interrupt({ id: m.messageId, message: m });
|
|
@@ -613,7 +617,16 @@ export class BotRunner {
|
|
|
613
617
|
}
|
|
614
618
|
};
|
|
615
619
|
const published = new TurnPublisher({
|
|
616
|
-
stream: () =>
|
|
620
|
+
stream: async () => {
|
|
621
|
+
const s = await ch.stream();
|
|
622
|
+
return {
|
|
623
|
+
append: (chunk) => s.append(chunk),
|
|
624
|
+
replace: (full) => s.replace(full),
|
|
625
|
+
done: (final) => s.done(final),
|
|
626
|
+
fail: (text) => s.fail(text),
|
|
627
|
+
cancel: () => ch.recall(s.messageId),
|
|
628
|
+
};
|
|
629
|
+
},
|
|
617
630
|
send: async (body) => {
|
|
618
631
|
await ch.send({ text: body });
|
|
619
632
|
},
|
|
@@ -722,7 +735,7 @@ export class BotRunner {
|
|
|
722
735
|
};
|
|
723
736
|
}
|
|
724
737
|
async ingestFollowup(m) {
|
|
725
|
-
if (m
|
|
738
|
+
if (isBotEcho(m, this.info.userId))
|
|
726
739
|
return null;
|
|
727
740
|
if (this.cantPost.has(m.channelId))
|
|
728
741
|
return null;
|
package/dist/bridge.d.ts
CHANGED
package/dist/bridge.js
CHANGED
|
@@ -19,7 +19,7 @@ import { reportCapabilities } from "./capabilities.js";
|
|
|
19
19
|
import { lastHermesApiHealth, probeHermesApi } from "./hermes-http.js";
|
|
20
20
|
import { lastTurnGlobal, turnSnapshot } from "./last-turn.js";
|
|
21
21
|
import { isBrainBackend } from "./config.js";
|
|
22
|
-
export const VERSION = "0.7.
|
|
22
|
+
export const VERSION = "0.7.21";
|
|
23
23
|
function hermesApiStatus(cfg) {
|
|
24
24
|
const api = lastHermesApiHealth();
|
|
25
25
|
return {
|
package/dist/inbound-files.js
CHANGED
|
@@ -56,7 +56,7 @@ export function hasInboundWork(body) {
|
|
|
56
56
|
const b = body;
|
|
57
57
|
if (isControlType(b.type))
|
|
58
58
|
return false;
|
|
59
|
-
if (typeof b.text === "string" && b.text.trim())
|
|
59
|
+
if (typeof b.text === "string" && b.text.trim() && b.text.trim() !== "…" && b.text.trim() !== "...")
|
|
60
60
|
return true;
|
|
61
61
|
if (parseAttachments(body).length > 0)
|
|
62
62
|
return true;
|
package/dist/mid-turn.d.ts
CHANGED
|
@@ -4,6 +4,21 @@ export interface FollowupDecision {
|
|
|
4
4
|
text: string;
|
|
5
5
|
}
|
|
6
6
|
export declare function isStopRequest(text: string): boolean;
|
|
7
|
+
/** Bare stream() placeholder. Must never count as a user follow-up. */
|
|
8
|
+
export declare function isPlaceholderText(text: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Own stream() posts come back as message.new on the same socket. If we treat
|
|
11
|
+
* those as mid-turn interrupts, the bot aborts itself on every "…" bubble,
|
|
12
|
+
* Hermes HTTP returns empty, CLI fallback starts a second job, and the chat
|
|
13
|
+
* fills with edited dots. Telegram never sees the bot's own outbound messages.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isBotEcho(m: {
|
|
16
|
+
senderUserId?: string;
|
|
17
|
+
body?: {
|
|
18
|
+
text?: unknown;
|
|
19
|
+
streaming?: unknown;
|
|
20
|
+
} | null;
|
|
21
|
+
}, botUserId: string): boolean;
|
|
7
22
|
export declare function coalesceFollowups(texts: string[]): FollowupDecision;
|
|
8
23
|
export declare function wrapInterruptedFollowup(text: string): string;
|
|
9
24
|
export declare function hermesSessionId(channelId: string): string;
|
package/dist/mid-turn.js
CHANGED
|
@@ -11,6 +11,27 @@ const STOP_RE = /^(?:\/)?(?:please\s+)?stop(?:\s+please)?[.!]?$/i;
|
|
|
11
11
|
export function isStopRequest(text) {
|
|
12
12
|
return STOP_RE.test((text || "").trim());
|
|
13
13
|
}
|
|
14
|
+
/** Bare stream() placeholder. Must never count as a user follow-up. */
|
|
15
|
+
export function isPlaceholderText(text) {
|
|
16
|
+
const t = (text || "").trim();
|
|
17
|
+
return t === "…" || t === "...";
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Own stream() posts come back as message.new on the same socket. If we treat
|
|
21
|
+
* those as mid-turn interrupts, the bot aborts itself on every "…" bubble,
|
|
22
|
+
* Hermes HTTP returns empty, CLI fallback starts a second job, and the chat
|
|
23
|
+
* fills with edited dots. Telegram never sees the bot's own outbound messages.
|
|
24
|
+
*/
|
|
25
|
+
export function isBotEcho(m, botUserId) {
|
|
26
|
+
if (botUserId && m.senderUserId === botUserId)
|
|
27
|
+
return true;
|
|
28
|
+
const body = m.body;
|
|
29
|
+
if (body && body.streaming === true)
|
|
30
|
+
return true;
|
|
31
|
+
if (typeof body?.text === "string" && isPlaceholderText(body.text))
|
|
32
|
+
return true;
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
14
35
|
export function coalesceFollowups(texts) {
|
|
15
36
|
const cleaned = texts.map((t) => (t || "").trim()).filter(Boolean);
|
|
16
37
|
if (cleaned.length === 0)
|
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>;
|
package/dist/tool-progress.js
CHANGED
|
@@ -445,7 +445,13 @@ export class TurnPublisher {
|
|
|
445
445
|
s.append(extra);
|
|
446
446
|
const parts = splitBubbles(final, this.maxBubbleChars);
|
|
447
447
|
if (!parts.length) {
|
|
448
|
-
|
|
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(() => { });
|
|
449
455
|
return;
|
|
450
456
|
}
|
|
451
457
|
const first = parts[0];
|
|
@@ -523,7 +529,11 @@ export class TurnPublisher {
|
|
|
523
529
|
this.progress = null;
|
|
524
530
|
const text = this.progressLines.join("\n");
|
|
525
531
|
this.progressLines = [];
|
|
526
|
-
if (s
|
|
532
|
+
if (!s)
|
|
533
|
+
return;
|
|
534
|
+
if (text)
|
|
527
535
|
await s.done(text).catch(() => { });
|
|
536
|
+
else
|
|
537
|
+
await s.cancel().catch(() => { });
|
|
528
538
|
}
|
|
529
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.21",
|
|
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",
|