openclaw-channel-bgos 0.6.1
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/README.md +87 -0
- package/dist/agent-hints.d.ts +29 -0
- package/dist/agent-hints.d.ts.map +1 -0
- package/dist/agent-hints.js +169 -0
- package/dist/agent-hints.js.map +1 -0
- package/dist/approval-handler.d.ts +71 -0
- package/dist/approval-handler.d.ts.map +1 -0
- package/dist/approval-handler.js +121 -0
- package/dist/approval-handler.js.map +1 -0
- package/dist/bgos-api.d.ts +111 -0
- package/dist/bgos-api.d.ts.map +1 -0
- package/dist/bgos-api.js +136 -0
- package/dist/bgos-api.js.map +1 -0
- package/dist/bgos-ws.d.ts +69 -0
- package/dist/bgos-ws.d.ts.map +1 -0
- package/dist/bgos-ws.js +172 -0
- package/dist/bgos-ws.js.map +1 -0
- package/dist/commands-sync.d.ts +24 -0
- package/dist/commands-sync.d.ts.map +1 -0
- package/dist/commands-sync.js +43 -0
- package/dist/commands-sync.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +36 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon-helpers.d.ts +109 -0
- package/dist/daemon-helpers.d.ts.map +1 -0
- package/dist/daemon-helpers.js +194 -0
- package/dist/daemon-helpers.js.map +1 -0
- package/dist/daemon.d.ts +23 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +645 -0
- package/dist/daemon.js.map +1 -0
- package/dist/default-commands.d.ts +17 -0
- package/dist/default-commands.d.ts.map +1 -0
- package/dist/default-commands.js +59 -0
- package/dist/default-commands.js.map +1 -0
- package/dist/inbound.d.ts +39 -0
- package/dist/inbound.d.ts.map +1 -0
- package/dist/inbound.js +39 -0
- package/dist/inbound.js.map +1 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +97 -0
- package/dist/index.js.map +1 -0
- package/dist/openclaw-gateway-client.d.ts +48 -0
- package/dist/openclaw-gateway-client.d.ts.map +1 -0
- package/dist/openclaw-gateway-client.js +109 -0
- package/dist/openclaw-gateway-client.js.map +1 -0
- package/dist/outbound.d.ts +160 -0
- package/dist/outbound.d.ts.map +1 -0
- package/dist/outbound.js +229 -0
- package/dist/outbound.js.map +1 -0
- package/dist/pair-cli.d.ts +30 -0
- package/dist/pair-cli.d.ts.map +1 -0
- package/dist/pair-cli.js +28 -0
- package/dist/pair-cli.js.map +1 -0
- package/dist/peers-server.d.ts +67 -0
- package/dist/peers-server.d.ts.map +1 -0
- package/dist/peers-server.js +226 -0
- package/dist/peers-server.js.map +1 -0
- package/dist/processed-ids.d.ts +35 -0
- package/dist/processed-ids.d.ts.map +1 -0
- package/dist/processed-ids.js +54 -0
- package/dist/processed-ids.js.map +1 -0
- package/dist/setup-entry.d.ts +44 -0
- package/dist/setup-entry.d.ts.map +1 -0
- package/dist/setup-entry.js +60 -0
- package/dist/setup-entry.js.map +1 -0
- package/dist/types.d.ts +279 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/openclaw.plugin.json +36 -0
- package/package.json +55 -0
package/dist/bgos-api.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { PairingRevokedError, } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Thin typed wrapper around the BGOS integration endpoints. All methods
|
|
5
|
+
* attach the X-BGOS-Pairing header from cfg.pairingToken.
|
|
6
|
+
*
|
|
7
|
+
* A 401 from any request is mapped to PairingRevokedError so callers
|
|
8
|
+
* (WS client, outbound adapter) can short-circuit and let the setup
|
|
9
|
+
* wizard prompt for re-pair.
|
|
10
|
+
*/
|
|
11
|
+
export class BgosApi {
|
|
12
|
+
http;
|
|
13
|
+
constructor(cfg) {
|
|
14
|
+
this.http = axios.create({
|
|
15
|
+
baseURL: cfg.baseUrl + "/api/v1",
|
|
16
|
+
headers: {
|
|
17
|
+
"X-BGOS-Pairing": cfg.pairingToken,
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
},
|
|
20
|
+
timeout: 30_000,
|
|
21
|
+
});
|
|
22
|
+
this.http.interceptors.response.use((r) => r, (err) => {
|
|
23
|
+
if (err?.response?.status === 401) {
|
|
24
|
+
return Promise.reject(new PairingRevokedError(err.response?.data?.message ?? "pairing rejected by BGOS"));
|
|
25
|
+
}
|
|
26
|
+
return Promise.reject(err);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/** GET /integrations/me — confirms token + touches last_seen_at.
|
|
30
|
+
* Includes assistant→agent_route bindings so the plugin can seed
|
|
31
|
+
* its dispatch map on cold start.
|
|
32
|
+
*/
|
|
33
|
+
async whoami() {
|
|
34
|
+
const r = await this.http.get("integrations/me");
|
|
35
|
+
return r.data;
|
|
36
|
+
}
|
|
37
|
+
/** Pair exchange (Public; does NOT need X-BGOS-Pairing). */
|
|
38
|
+
static async pairExchange(baseUrl, params) {
|
|
39
|
+
const base = baseUrl.replace(/\/+$/, "") + "/api/v1";
|
|
40
|
+
const r = await axios.post(`${base}/integrations/pair-exchange`, params, {
|
|
41
|
+
timeout: 15_000,
|
|
42
|
+
});
|
|
43
|
+
return r.data;
|
|
44
|
+
}
|
|
45
|
+
/** Plugin pushes (or updates) the agent catalog for this pairing. */
|
|
46
|
+
async pushAgentCatalog(pairingId, agents) {
|
|
47
|
+
await this.http.post(`integrations/pairings/${pairingId}/agent-catalog`, {
|
|
48
|
+
agents,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/** Plugin replaces the slash-command manifest for a single bound assistant. */
|
|
52
|
+
async putCommands(assistantId, commands) {
|
|
53
|
+
await this.http.put(`integrations/assistants/${assistantId}/commands`, { commands });
|
|
54
|
+
}
|
|
55
|
+
/** REST backfill after a WS reconnect. */
|
|
56
|
+
async inboundSince(sinceMessageId) {
|
|
57
|
+
const r = await this.http.get("integrations/inbound", {
|
|
58
|
+
params: { since_message_id: sinceMessageId },
|
|
59
|
+
});
|
|
60
|
+
return r.data;
|
|
61
|
+
}
|
|
62
|
+
/** Fetch the recent message history for a chat — used by the daemon to
|
|
63
|
+
* rebuild conversation context before dispatching to a stateless
|
|
64
|
+
* gateway. Backend returns up to 100 entries ASC by created_at. */
|
|
65
|
+
async getMessages(chatId, userId) {
|
|
66
|
+
const r = await this.http.get(`chats/${chatId}/messages`, {
|
|
67
|
+
params: { userId },
|
|
68
|
+
});
|
|
69
|
+
const rows = r.data?.messages;
|
|
70
|
+
return Array.isArray(rows) ? rows : [];
|
|
71
|
+
}
|
|
72
|
+
/** Agent reply — assistant message with optional inline buttons/approval. */
|
|
73
|
+
async postMessage(payload) {
|
|
74
|
+
const r = await this.http.post("messages", payload);
|
|
75
|
+
return r.data;
|
|
76
|
+
}
|
|
77
|
+
/** Request a presigned PUT for a file ≥500 KB that the agent wants to send. */
|
|
78
|
+
async createUploadUrl(params) {
|
|
79
|
+
const r = await this.http.post("integrations/files/upload-url", {
|
|
80
|
+
filename: params.filename,
|
|
81
|
+
mime_type: params.mimeType,
|
|
82
|
+
size: params.size,
|
|
83
|
+
});
|
|
84
|
+
return r.data;
|
|
85
|
+
}
|
|
86
|
+
/** List this pairing's active/paired state. Mostly used in setup wizard. */
|
|
87
|
+
async listPairings() {
|
|
88
|
+
const r = await this.http.get("integrations/pairings");
|
|
89
|
+
return r.data;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A2A peer discovery for an OpenClaw agent. Returns the user's other
|
|
93
|
+
* assistants the caller is allowed to reach (filtered by the
|
|
94
|
+
* agent_introductions table). The caller's assistant id is sent via
|
|
95
|
+
* X-Caller-Assistant-Id; backend uses it to scope visibility +
|
|
96
|
+
* enforce per-pair introduction rules.
|
|
97
|
+
*
|
|
98
|
+
* Used by the daemon's peers HTTP server (peers_list) so OpenClaw
|
|
99
|
+
* agents can discover Ava/Mario/etc. by name before initiating a
|
|
100
|
+
* peer message via sendToPeer().
|
|
101
|
+
*/
|
|
102
|
+
async listPeers(callerAssistantId) {
|
|
103
|
+
const r = await this.http.get("peers", {
|
|
104
|
+
headers: { "X-Caller-Assistant-Id": String(callerAssistantId) },
|
|
105
|
+
});
|
|
106
|
+
return Array.isArray(r.data) ? r.data : [];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Initiate (or continue) a peer side-thread from this OpenClaw agent
|
|
110
|
+
* to another BGOS assistant. Backend creates/reuses the a2a chat,
|
|
111
|
+
* persists the message, and emits inbound_message to the target's
|
|
112
|
+
* channel adapter with peerConversationId + turnState set.
|
|
113
|
+
*
|
|
114
|
+
* @param parentMessageId Required by the backend: anchors the
|
|
115
|
+
* SideConversationCard in the originating chat. Daemon picks the
|
|
116
|
+
* most-recent inbound for this caller's assistant when the agent
|
|
117
|
+
* doesn't supply one (see peers-server.ts).
|
|
118
|
+
*/
|
|
119
|
+
async sendToPeer(callerAssistantId, targetAssistantId, params) {
|
|
120
|
+
const r = await this.http.post(`peers/${targetAssistantId}/send`, {
|
|
121
|
+
text: params.text,
|
|
122
|
+
parentMessageId: params.parentMessageId,
|
|
123
|
+
...(params.waitForReply !== undefined && {
|
|
124
|
+
waitForReply: params.waitForReply,
|
|
125
|
+
}),
|
|
126
|
+
...(params.timeoutSeconds !== undefined && {
|
|
127
|
+
timeoutSeconds: params.timeoutSeconds,
|
|
128
|
+
}),
|
|
129
|
+
...(params.turnState !== undefined && { turnState: params.turnState }),
|
|
130
|
+
}, {
|
|
131
|
+
headers: { "X-Caller-Assistant-Id": String(callerAssistantId) },
|
|
132
|
+
});
|
|
133
|
+
return r.data;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=bgos-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bgos-api.js","sourceRoot":"","sources":["../src/bgos-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { BgosApi } from "./bgos-api.js";
|
|
2
|
+
import { type AssistantBoundPayload, type AssistantUnboundPayload, type CallbackResultPayload, type CommandsUpdatedPayload, type InboundMessagePayload, type PairReadyPayload, type PairingRevokedPayload, type PluginConfig } from "./types.js";
|
|
3
|
+
type EventMap = {
|
|
4
|
+
inbound_message: [InboundMessagePayload];
|
|
5
|
+
commands_updated: [CommandsUpdatedPayload];
|
|
6
|
+
pair_ready: [PairReadyPayload];
|
|
7
|
+
assistant_bound: [AssistantBoundPayload];
|
|
8
|
+
assistant_unbound: [AssistantUnboundPayload];
|
|
9
|
+
pairing_revoked: [PairingRevokedPayload];
|
|
10
|
+
callback_result: [CallbackResultPayload];
|
|
11
|
+
error: [Error];
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Socket.IO client against BGOS. Handshake passes pairingToken as a query
|
|
15
|
+
* param; backend validates via bcrypt and joins the client to
|
|
16
|
+
* pairing:<id> + every assistant:<id> the pairing owns.
|
|
17
|
+
*
|
|
18
|
+
* Reconnect: exponential backoff 1s → 30s. On reconnect, the plugin
|
|
19
|
+
* should call triggerBackfill() which hits GET /integrations/inbound?since=...
|
|
20
|
+
* and re-emits missed user messages through the same inbound_message
|
|
21
|
+
* event — callers handle live + backfill identically.
|
|
22
|
+
*/
|
|
23
|
+
export declare class BgosWs {
|
|
24
|
+
private readonly cfg;
|
|
25
|
+
private readonly api;
|
|
26
|
+
private readonly emitter;
|
|
27
|
+
private socket;
|
|
28
|
+
private lastSeenMessageId;
|
|
29
|
+
private reconnectAttempts;
|
|
30
|
+
private manualClose;
|
|
31
|
+
/**
|
|
32
|
+
* De-dup cache for inbound message IDs. Without this, the same
|
|
33
|
+
* `inbound_message` can fire downstream twice — observed in prod
|
|
34
|
+
* 2026-05-10 when the daemon's ~33s reconnect cycle caused
|
|
35
|
+
* `triggerBackfill()` to re-emit live messages whose lastSeenMessageId
|
|
36
|
+
* cursor hadn't yet settled. Bounded at 500 entries (FIFO eviction).
|
|
37
|
+
*/
|
|
38
|
+
private readonly processedIds;
|
|
39
|
+
constructor(cfg: PluginConfig, api: BgosApi);
|
|
40
|
+
on<K extends keyof EventMap>(event: K, listener: (...args: EventMap[K]) => void): this;
|
|
41
|
+
off<K extends keyof EventMap>(event: K, listener: (...args: EventMap[K]) => void): this;
|
|
42
|
+
connect(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Explicit reconnect-triggered backfill. Also callable on startup.
|
|
45
|
+
*
|
|
46
|
+
* COLD-START GUARD: when `lastSeenMessageId === 0` at the start of this
|
|
47
|
+
* call, treat it as a fresh-process boot. We still fetch the inbound
|
|
48
|
+
* history to seed the cursor + dedup cache, but we do NOT emit the
|
|
49
|
+
* messages downstream. Without this guard every daemon restart re-plays
|
|
50
|
+
* the entire chat history (observed in prod 2026-05-10: chat 917 saw 20+
|
|
51
|
+
* replies to old messages within a 2-minute window after a daemon
|
|
52
|
+
* upgrade-restart).
|
|
53
|
+
*
|
|
54
|
+
* Trade-off: messages sent DURING the daemon-restart window are dropped.
|
|
55
|
+
* Better fix: persist `lastSeenMessageId` to disk so cold start can
|
|
56
|
+
* resume from where the previous process left off — deferred follow-up.
|
|
57
|
+
*/
|
|
58
|
+
triggerBackfill(): Promise<void>;
|
|
59
|
+
setLastSeen(messageId: number): void;
|
|
60
|
+
disconnect(): void;
|
|
61
|
+
/**
|
|
62
|
+
* Translate the wire-level payload (snake_case) into our camelCase type.
|
|
63
|
+
* Backend already emits camelCase via the helper service, but defensively
|
|
64
|
+
* handle both so we don't drop messages if someone changes the emitter.
|
|
65
|
+
*/
|
|
66
|
+
private normalizeInbound;
|
|
67
|
+
}
|
|
68
|
+
export {};
|
|
69
|
+
//# sourceMappingURL=bgos-ws.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bgos-ws.d.ts","sourceRoot":"","sources":["../src/bgos-ws.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AAEpB,KAAK,QAAQ,GAAG;IACd,eAAe,EAAE,CAAC,qBAAqB,CAAC,CAAC;IACzC,gBAAgB,EAAE,CAAC,sBAAsB,CAAC,CAAC;IAC3C,UAAU,EAAE,CAAC,gBAAgB,CAAC,CAAC;IAC/B,eAAe,EAAE,CAAC,qBAAqB,CAAC,CAAC;IACzC,iBAAiB,EAAE,CAAC,uBAAuB,CAAC,CAAC;IAC7C,eAAe,EAAE,CAAC,qBAAqB,CAAC,CAAC;IACzC,eAAe,EAAE,CAAC,qBAAqB,CAAC,CAAC;IACzC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;CAChB,CAAC;AAEF;;;;;;;;;GASG;AACH,qBAAa,MAAM;IAgBf,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAhBtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,WAAW,CAAS;IAC5B;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA8B;gBAGxC,GAAG,EAAE,YAAY,EACjB,GAAG,EAAE,OAAO;IAG/B,EAAE,CAAC,CAAC,SAAS,MAAM,QAAQ,EACzB,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,GACvC,IAAI;IAKP,GAAG,CAAC,CAAC,SAAS,MAAM,QAAQ,EAC1B,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,GACvC,IAAI;IAKD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAiE9B;;;;;;;;;;;;;;OAcG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BtC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAMpC,UAAU,IAAI,IAAI;IAMlB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;CA2BzB"}
|
package/dist/bgos-ws.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { io } from "socket.io-client";
|
|
2
|
+
import { EventEmitter } from "node:events";
|
|
3
|
+
import { ProcessedIdsCache } from "./processed-ids.js";
|
|
4
|
+
import { PairingRevokedError, } from "./types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Socket.IO client against BGOS. Handshake passes pairingToken as a query
|
|
7
|
+
* param; backend validates via bcrypt and joins the client to
|
|
8
|
+
* pairing:<id> + every assistant:<id> the pairing owns.
|
|
9
|
+
*
|
|
10
|
+
* Reconnect: exponential backoff 1s → 30s. On reconnect, the plugin
|
|
11
|
+
* should call triggerBackfill() which hits GET /integrations/inbound?since=...
|
|
12
|
+
* and re-emits missed user messages through the same inbound_message
|
|
13
|
+
* event — callers handle live + backfill identically.
|
|
14
|
+
*/
|
|
15
|
+
export class BgosWs {
|
|
16
|
+
cfg;
|
|
17
|
+
api;
|
|
18
|
+
emitter = new EventEmitter();
|
|
19
|
+
socket = null;
|
|
20
|
+
lastSeenMessageId = 0;
|
|
21
|
+
reconnectAttempts = 0;
|
|
22
|
+
manualClose = false;
|
|
23
|
+
/**
|
|
24
|
+
* De-dup cache for inbound message IDs. Without this, the same
|
|
25
|
+
* `inbound_message` can fire downstream twice — observed in prod
|
|
26
|
+
* 2026-05-10 when the daemon's ~33s reconnect cycle caused
|
|
27
|
+
* `triggerBackfill()` to re-emit live messages whose lastSeenMessageId
|
|
28
|
+
* cursor hadn't yet settled. Bounded at 500 entries (FIFO eviction).
|
|
29
|
+
*/
|
|
30
|
+
processedIds = new ProcessedIdsCache(500);
|
|
31
|
+
constructor(cfg, api) {
|
|
32
|
+
this.cfg = cfg;
|
|
33
|
+
this.api = api;
|
|
34
|
+
}
|
|
35
|
+
on(event, listener) {
|
|
36
|
+
this.emitter.on(event, listener);
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
off(event, listener) {
|
|
40
|
+
this.emitter.off(event, listener);
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
async connect() {
|
|
44
|
+
this.manualClose = false;
|
|
45
|
+
const socket = io(this.cfg.baseUrl, {
|
|
46
|
+
path: "/socket.io",
|
|
47
|
+
query: { pairingToken: this.cfg.pairingToken },
|
|
48
|
+
transports: ["websocket"],
|
|
49
|
+
reconnection: true,
|
|
50
|
+
reconnectionDelay: this.cfg.reconnect.initialDelayMs,
|
|
51
|
+
reconnectionDelayMax: this.cfg.reconnect.maxDelayMs,
|
|
52
|
+
reconnectionAttempts: Infinity,
|
|
53
|
+
});
|
|
54
|
+
socket.on("connect", () => {
|
|
55
|
+
this.reconnectAttempts = 0;
|
|
56
|
+
});
|
|
57
|
+
socket.on("reconnect", () => void this.triggerBackfill());
|
|
58
|
+
socket.on("inbound_message", (payload) => {
|
|
59
|
+
const msg = this.normalizeInbound(payload);
|
|
60
|
+
if (!msg)
|
|
61
|
+
return;
|
|
62
|
+
// Idempotency gate — skip if we've already dispatched this messageId.
|
|
63
|
+
// See ProcessedIdsCache for the prod incident this prevents.
|
|
64
|
+
if (!this.processedIds.markIfFirstTime(msg.messageId)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (msg.messageId > this.lastSeenMessageId) {
|
|
68
|
+
this.lastSeenMessageId = msg.messageId;
|
|
69
|
+
}
|
|
70
|
+
this.emitter.emit("inbound_message", msg);
|
|
71
|
+
});
|
|
72
|
+
socket.on("commands_updated", (p) => this.emitter.emit("commands_updated", p));
|
|
73
|
+
socket.on("pair_ready", (p) => this.emitter.emit("pair_ready", p));
|
|
74
|
+
socket.on("assistant_bound", (p) => this.emitter.emit("assistant_bound", p));
|
|
75
|
+
socket.on("assistant_unbound", (p) => this.emitter.emit("assistant_unbound", p));
|
|
76
|
+
socket.on("pairing_revoked", (p) => {
|
|
77
|
+
this.emitter.emit("pairing_revoked", p);
|
|
78
|
+
this.emitter.emit("error", new PairingRevokedError("Pairing revoked by backend"));
|
|
79
|
+
});
|
|
80
|
+
socket.on("callback_result", (p) => this.emitter.emit("callback_result", p));
|
|
81
|
+
socket.on("connect_error", (err) => {
|
|
82
|
+
this.reconnectAttempts++;
|
|
83
|
+
this.emitter.emit("error", err);
|
|
84
|
+
});
|
|
85
|
+
this.socket = socket;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Explicit reconnect-triggered backfill. Also callable on startup.
|
|
89
|
+
*
|
|
90
|
+
* COLD-START GUARD: when `lastSeenMessageId === 0` at the start of this
|
|
91
|
+
* call, treat it as a fresh-process boot. We still fetch the inbound
|
|
92
|
+
* history to seed the cursor + dedup cache, but we do NOT emit the
|
|
93
|
+
* messages downstream. Without this guard every daemon restart re-plays
|
|
94
|
+
* the entire chat history (observed in prod 2026-05-10: chat 917 saw 20+
|
|
95
|
+
* replies to old messages within a 2-minute window after a daemon
|
|
96
|
+
* upgrade-restart).
|
|
97
|
+
*
|
|
98
|
+
* Trade-off: messages sent DURING the daemon-restart window are dropped.
|
|
99
|
+
* Better fix: persist `lastSeenMessageId` to disk so cold start can
|
|
100
|
+
* resume from where the previous process left off — deferred follow-up.
|
|
101
|
+
*/
|
|
102
|
+
async triggerBackfill() {
|
|
103
|
+
try {
|
|
104
|
+
const isColdStart = this.lastSeenMessageId === 0;
|
|
105
|
+
const { messages } = await this.api.inboundSince(this.lastSeenMessageId);
|
|
106
|
+
for (const m of messages) {
|
|
107
|
+
// Same idempotency gate as the live WS handler. Backfill races
|
|
108
|
+
// against in-flight live emits during reconnect — without this
|
|
109
|
+
// check, a message that landed via WS just before disconnect can
|
|
110
|
+
// re-fire here and produce a duplicate dispatch.
|
|
111
|
+
if (!this.processedIds.markIfFirstTime(m.messageId)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (m.messageId > this.lastSeenMessageId) {
|
|
115
|
+
this.lastSeenMessageId = m.messageId;
|
|
116
|
+
}
|
|
117
|
+
if (isColdStart) {
|
|
118
|
+
// Cold start — seed only; never re-dispatch history.
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
this.emitter.emit("inbound_message", m);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
this.emitter.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
setLastSeen(messageId) {
|
|
129
|
+
if (messageId > this.lastSeenMessageId) {
|
|
130
|
+
this.lastSeenMessageId = messageId;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
disconnect() {
|
|
134
|
+
this.manualClose = true;
|
|
135
|
+
this.socket?.disconnect();
|
|
136
|
+
this.socket = null;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Translate the wire-level payload (snake_case) into our camelCase type.
|
|
140
|
+
* Backend already emits camelCase via the helper service, but defensively
|
|
141
|
+
* handle both so we don't drop messages if someone changes the emitter.
|
|
142
|
+
*/
|
|
143
|
+
normalizeInbound(raw) {
|
|
144
|
+
if (!raw || typeof raw !== "object")
|
|
145
|
+
return null;
|
|
146
|
+
const r = raw;
|
|
147
|
+
const toInt = (v) => {
|
|
148
|
+
if (typeof v === "number")
|
|
149
|
+
return v;
|
|
150
|
+
if (typeof v === "string" && /^\d+$/.test(v))
|
|
151
|
+
return Number(v);
|
|
152
|
+
return 0;
|
|
153
|
+
};
|
|
154
|
+
const assistantId = toInt(r.assistantId ?? r.assistant_id);
|
|
155
|
+
const chatId = toInt(r.chatId ?? r.chat_id);
|
|
156
|
+
const messageId = toInt(r.messageId ?? r.message_id);
|
|
157
|
+
if (!assistantId || !chatId || !messageId)
|
|
158
|
+
return null;
|
|
159
|
+
return {
|
|
160
|
+
assistantId,
|
|
161
|
+
chatId,
|
|
162
|
+
messageId,
|
|
163
|
+
userId: String(r.userId ?? r.user_id ?? ""),
|
|
164
|
+
text: String(r.text ?? ""),
|
|
165
|
+
files: Array.isArray(r.files) ? r.files : [],
|
|
166
|
+
messageType: (r.messageType ?? r.message_type ?? "standard"),
|
|
167
|
+
commandName: (r.commandName ?? r.command_name ?? undefined),
|
|
168
|
+
commandArgs: (r.commandArgs ?? r.command_args ?? undefined),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=bgos-ws.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bgos-ws.js","sourceRoot":"","sources":["../src/bgos-ws.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BgosApi } from "./bgos-api.js";
|
|
2
|
+
import type { CommandManifestEntry } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Per-assistant command manifest sync. Debounces rapid changes into at
|
|
5
|
+
* most one PUT every DEBOUNCE_MS. Analogous to Telegram plugin's
|
|
6
|
+
* setMyCommands flow (see bot-native-commands.ts in OpenClaw's telegram
|
|
7
|
+
* extension).
|
|
8
|
+
*/
|
|
9
|
+
export declare class CommandsSync {
|
|
10
|
+
private readonly api;
|
|
11
|
+
private static readonly DEBOUNCE_MS;
|
|
12
|
+
private readonly pending;
|
|
13
|
+
private timers;
|
|
14
|
+
constructor(api: BgosApi);
|
|
15
|
+
/**
|
|
16
|
+
* Queue a replacement manifest for an assistant. Coalesces multiple
|
|
17
|
+
* calls within DEBOUNCE_MS into a single PUT.
|
|
18
|
+
*/
|
|
19
|
+
schedule(assistantId: number, commands: CommandManifestEntry[]): void;
|
|
20
|
+
/** Force-sync all pending — useful on graceful shutdown or pair-ready. */
|
|
21
|
+
flushAll(): Promise<void>;
|
|
22
|
+
private flush;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=commands-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands-sync.d.ts","sourceRoot":"","sources":["../src/commands-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD;;;;;GAKG;AACH,qBAAa,YAAY;IAMX,OAAO,CAAC,QAAQ,CAAC,GAAG;IALhC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAQ;IAE3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6C;IACrE,OAAO,CAAC,MAAM,CAAqC;gBAEtB,GAAG,EAAE,OAAO;IAEzC;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,EAAE,GAAG,IAAI;IAUrE,0EAA0E;IACpE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAKjB,KAAK;CAOpB"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-assistant command manifest sync. Debounces rapid changes into at
|
|
3
|
+
* most one PUT every DEBOUNCE_MS. Analogous to Telegram plugin's
|
|
4
|
+
* setMyCommands flow (see bot-native-commands.ts in OpenClaw's telegram
|
|
5
|
+
* extension).
|
|
6
|
+
*/
|
|
7
|
+
export class CommandsSync {
|
|
8
|
+
api;
|
|
9
|
+
static DEBOUNCE_MS = 2000;
|
|
10
|
+
pending = new Map();
|
|
11
|
+
timers = new Map();
|
|
12
|
+
constructor(api) {
|
|
13
|
+
this.api = api;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Queue a replacement manifest for an assistant. Coalesces multiple
|
|
17
|
+
* calls within DEBOUNCE_MS into a single PUT.
|
|
18
|
+
*/
|
|
19
|
+
schedule(assistantId, commands) {
|
|
20
|
+
this.pending.set(assistantId, commands);
|
|
21
|
+
const existing = this.timers.get(assistantId);
|
|
22
|
+
if (existing)
|
|
23
|
+
clearTimeout(existing);
|
|
24
|
+
const t = setTimeout(() => {
|
|
25
|
+
void this.flush(assistantId);
|
|
26
|
+
}, CommandsSync.DEBOUNCE_MS);
|
|
27
|
+
this.timers.set(assistantId, t);
|
|
28
|
+
}
|
|
29
|
+
/** Force-sync all pending — useful on graceful shutdown or pair-ready. */
|
|
30
|
+
async flushAll() {
|
|
31
|
+
const ids = [...this.pending.keys()];
|
|
32
|
+
await Promise.all(ids.map((id) => this.flush(id)));
|
|
33
|
+
}
|
|
34
|
+
async flush(assistantId) {
|
|
35
|
+
const commands = this.pending.get(assistantId);
|
|
36
|
+
this.pending.delete(assistantId);
|
|
37
|
+
this.timers.delete(assistantId);
|
|
38
|
+
if (!commands)
|
|
39
|
+
return;
|
|
40
|
+
await this.api.putCommands(assistantId, commands);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=commands-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands-sync.js","sourceRoot":"","sources":["../src/commands-sync.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,OAAO,YAAY;IAMM;IALrB,MAAM,CAAU,WAAW,GAAG,IAAI,CAAC;IAE1B,OAAO,GAAG,IAAI,GAAG,EAAkC,CAAC;IAC7D,MAAM,GAAG,IAAI,GAAG,EAA0B,CAAC;IAEnD,YAA6B,GAAY;QAAZ,QAAG,GAAH,GAAG,CAAS;IAAG,CAAC;IAE7C;;;OAGG;IACH,QAAQ,CAAC,WAAmB,EAAE,QAAgC;QAC5D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC9C,IAAI,QAAQ;YAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE;YACxB,KAAK,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC/B,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,0EAA0E;IAC1E,KAAK,CAAC,QAAQ;QACZ,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QACrC,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACrD,CAAC;IAEO,KAAK,CAAC,KAAK,CAAC,WAAmB;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAChC,IAAI,CAAC,QAAQ;YAAE,OAAO;QACtB,MAAM,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACpD,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAS/C,wBAAgB,iBAAiB,IAAI,YAAY,CAOhD;AAaD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,CAUlE"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const DEFAULT_BASE_URL = "https://api.brandgrowthos.ai";
|
|
3
|
+
const envSchema = z.object({
|
|
4
|
+
BGOS_BASE_URL: z.string().url().optional(),
|
|
5
|
+
BGOS_PAIRING_TOKEN: z.string().min(20),
|
|
6
|
+
});
|
|
7
|
+
export function loadConfigFromEnv() {
|
|
8
|
+
const env = envSchema.parse(process.env);
|
|
9
|
+
return {
|
|
10
|
+
baseUrl: (env.BGOS_BASE_URL ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
|
|
11
|
+
pairingToken: env.BGOS_PAIRING_TOKEN,
|
|
12
|
+
reconnect: { initialDelayMs: 1000, maxDelayMs: 30000 },
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const cfgSchema = z.object({
|
|
16
|
+
baseUrl: z.string().url().optional(),
|
|
17
|
+
pairingToken: z.string().min(20),
|
|
18
|
+
reconnect: z
|
|
19
|
+
.object({
|
|
20
|
+
initialDelayMs: z.number().int().positive().optional(),
|
|
21
|
+
maxDelayMs: z.number().int().positive().optional(),
|
|
22
|
+
})
|
|
23
|
+
.optional(),
|
|
24
|
+
});
|
|
25
|
+
export function loadConfigFromPluginCfg(cfg) {
|
|
26
|
+
const parsed = cfgSchema.parse(cfg);
|
|
27
|
+
return {
|
|
28
|
+
baseUrl: (parsed.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
|
|
29
|
+
pairingToken: parsed.pairingToken,
|
|
30
|
+
reconnect: {
|
|
31
|
+
initialDelayMs: parsed.reconnect?.initialDelayMs ?? 1000,
|
|
32
|
+
maxDelayMs: parsed.reconnect?.maxDelayMs ?? 30000,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,gBAAgB,GAAG,8BAA8B,CAAC;AAExD,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IACzB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC1C,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;CACvC,CAAC,CAAC;AAEH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACzC,OAAO;QACL,OAAO,EAAE,CAAC,GAAG,CAAC,aAAa,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACpE,YAAY,EAAE,GAAG,CAAC,kBAAkB;QACpC,SAAS,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE;KACvD,CAAC;AACJ,CAAC;AAED,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IACzB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACpC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;IAChC,SAAS,EAAE,CAAC;SACT,MAAM,CAAC;QACN,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;QACtD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;KACnD,CAAC;SACD,QAAQ,EAAE;CACd,CAAC,CAAC;AAEH,MAAM,UAAU,uBAAuB,CAAC,GAAY;IAClD,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,OAAO;QACL,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACjE,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,SAAS,EAAE;YACT,cAAc,EAAE,MAAM,CAAC,SAAS,EAAE,cAAc,IAAI,IAAI;YACxD,UAAU,EAAE,MAAM,CAAC,SAAS,EAAE,UAAU,IAAI,KAAK;SAClD;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { BgosMessageEnvelope, ChatMessage, ChatMessageContent, InboundFile } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Convert an inbound user message (text + files) into one OpenAI-compat
|
|
4
|
+
* ChatMessage.
|
|
5
|
+
*
|
|
6
|
+
* - Images become `image_url` blocks for vision-capable models.
|
|
7
|
+
* - Non-image files become text markers `[user attached: name — mime — url]`
|
|
8
|
+
* so non-vision agents still know something was attached.
|
|
9
|
+
* - URL-less files (legacy inline-only entries) are skipped — we cannot
|
|
10
|
+
* reference them from a stateless dispatch.
|
|
11
|
+
*
|
|
12
|
+
* If the message ends up with no usable content blocks, we collapse to
|
|
13
|
+
* the original text (preserves the empty-string contract upstream).
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildInboundChatMessage(params: {
|
|
16
|
+
text: string;
|
|
17
|
+
files: InboundFile[];
|
|
18
|
+
}): ChatMessage;
|
|
19
|
+
/**
|
|
20
|
+
* Build an optional 1-token marker line summarising read-only context
|
|
21
|
+
* fields on a BGOS message envelope. Returns "" when there's nothing to
|
|
22
|
+
* surface.
|
|
23
|
+
*
|
|
24
|
+
* Used to decorate prior history turns so the agent sees that a message
|
|
25
|
+
* was a reply / forward / pinned without bloating the content blocks.
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildContextMarkers(env: BgosMessageEnvelope["message"]): string;
|
|
28
|
+
/** Map a BgosMessageEnvelope.files row into the InboundFile shape. */
|
|
29
|
+
export declare function envelopeFileToInbound(f: NonNullable<BgosMessageEnvelope["message"]["files"]>[number]): InboundFile | null;
|
|
30
|
+
/**
|
|
31
|
+
* Map BGOS chat-history rows to the OpenAI-compat `messages` array.
|
|
32
|
+
* Skips internal `agent_error` turns; keeps file-only / voice-only
|
|
33
|
+
* turns as content blocks (image_url for images, text markers for the
|
|
34
|
+
* rest) so prior multimodal context survives. Caps at `limit`
|
|
35
|
+
* most-recent entries.
|
|
36
|
+
*/
|
|
37
|
+
export declare function mapToOpenAIRoles(rows: BgosMessageEnvelope[], limit: number): ChatMessage[];
|
|
38
|
+
/** Deep-equality helper used to dedupe an already-present last user
|
|
39
|
+
* turn against the live inbound. JSON.stringify is acceptable here —
|
|
40
|
+
* content blocks are tiny and rare to mismatch. */
|
|
41
|
+
export declare function chatMessageContentEqual(a: ChatMessageContent, b: ChatMessageContent): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Bridge-local commands (`/new`, `/retry`, `/status`) are intercepted
|
|
44
|
+
* by the plugin BEFORE forwarding to the gateway. See
|
|
45
|
+
* bgos-agent-capabilities.md §7 — these commands have channel-local
|
|
46
|
+
* semantics (history reset, re-dispatch, liveness probe) that must NOT
|
|
47
|
+
* roll through to the agent.
|
|
48
|
+
*
|
|
49
|
+
* The handler is a tiny state machine:
|
|
50
|
+
*
|
|
51
|
+
* - `/new` records a "history-reset point" (the inbound messageId).
|
|
52
|
+
* Subsequent dispatches filter the BGOS history fetch to entries
|
|
53
|
+
* strictly newer than this point — effectively wiping context for
|
|
54
|
+
* the chat without touching the database.
|
|
55
|
+
*
|
|
56
|
+
* - `/retry` re-dispatches the most-recent user turn to the gateway
|
|
57
|
+
* (looked up via the BGOS history fetch), so the user can ask the
|
|
58
|
+
* agent to take another swing without re-typing.
|
|
59
|
+
*
|
|
60
|
+
* - `/status` replies inline with the daemon's current health (route,
|
|
61
|
+
* gateway up/down, last reply latency, pairing id). No network round
|
|
62
|
+
* trip — pure introspection.
|
|
63
|
+
*/
|
|
64
|
+
export type BridgeCommandName = "new" | "retry" | "status";
|
|
65
|
+
export declare const BRIDGE_LOCAL_COMMANDS: ReadonlySet<BridgeCommandName>;
|
|
66
|
+
export declare function isBridgeLocalCommand(cmd: string): cmd is BridgeCommandName;
|
|
67
|
+
/**
|
|
68
|
+
* Per-chat history-reset cursor produced by `/new`. Subsequent dispatches
|
|
69
|
+
* read this map and skip rows with `id <= cursor`. Stays in-process —
|
|
70
|
+
* a daemon restart wipes the resets, which is fine: the worst case is
|
|
71
|
+
* one extra turn of unwanted history and the user can `/new` again.
|
|
72
|
+
*/
|
|
73
|
+
export declare class HistoryResetState {
|
|
74
|
+
private readonly cursors;
|
|
75
|
+
/** Record a reset point for `chatId`. Inclusive — the inbound `/new`
|
|
76
|
+
* message itself is filtered out so the agent doesn't see it. */
|
|
77
|
+
record(chatId: number, messageId: number): void;
|
|
78
|
+
/** Filter a list of envelopes for one chat, dropping entries that are
|
|
79
|
+
* at or older than the most-recent `/new` cursor. */
|
|
80
|
+
filterRows(chatId: number, rows: BgosMessageEnvelope[]): BgosMessageEnvelope[];
|
|
81
|
+
/** Forget the cursor for a chat (e.g. on chat delete). */
|
|
82
|
+
clear(chatId: number): void;
|
|
83
|
+
/** Test introspection. */
|
|
84
|
+
size(): number;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Build the per-turn system message that surfaces shared-recipient
|
|
88
|
+
* context to the agent. Returns null when the inbound message isn't a
|
|
89
|
+
* shared-assistant scenario (no addendum needed).
|
|
90
|
+
*
|
|
91
|
+
* The message goes into the `messages` array right before the live
|
|
92
|
+
* user turn so the agent sees it in-prompt and can key per-user data
|
|
93
|
+
* off `userId` to keep recipients isolated.
|
|
94
|
+
*/
|
|
95
|
+
export declare function buildSharedRecipientSystem(params: {
|
|
96
|
+
isSharedRecipient?: boolean;
|
|
97
|
+
userId: string;
|
|
98
|
+
shareOwnerUserId?: string | null;
|
|
99
|
+
}): ChatMessage | null;
|
|
100
|
+
/**
|
|
101
|
+
* Find the most-recent user turn (text + files) in a sequence of
|
|
102
|
+
* envelopes — used by `/retry` to look up the prior turn to re-dispatch.
|
|
103
|
+
* Excludes the `/retry` envelope itself when its messageId is supplied.
|
|
104
|
+
*/
|
|
105
|
+
export declare function findMostRecentUserTurn(rows: BgosMessageEnvelope[], excludeMessageId?: number): {
|
|
106
|
+
text: string;
|
|
107
|
+
files: InboundFile[];
|
|
108
|
+
} | null;
|
|
109
|
+
//# sourceMappingURL=daemon-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-helpers.d.ts","sourceRoot":"","sources":["../src/daemon-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB,GAAG,WAAW,CAwBd;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,mBAAmB,CAAC,SAAS,CAAC,GAAG,MAAM,CAQ/E;AAED,sEAAsE;AACtE,wBAAgB,qBAAqB,CACnC,CAAC,EAAE,WAAW,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAC9D,WAAW,GAAG,IAAI,CASpB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,mBAAmB,EAAE,EAC3B,KAAK,EAAE,MAAM,GACZ,WAAW,EAAE,CA2Bf;AAED;;oDAEoD;AACpD,wBAAgB,uBAAuB,CACrC,CAAC,EAAE,kBAAkB,EACrB,CAAC,EAAE,kBAAkB,GACpB,OAAO,CAGT;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE3D,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,iBAAiB,CAI/D,CAAC;AAEH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,GACV,GAAG,IAAI,iBAAiB,CAE1B;AAED;;;;;GAKG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IAErD;sEACkE;IAClE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAI/C;0DACsD;IACtD,UAAU,CACR,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,mBAAmB,EAAE,GAC1B,mBAAmB,EAAE;IAMxB,0DAA0D;IAC1D,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI3B,0BAA0B;IAC1B,IAAI,IAAI,MAAM;CAGf;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IACjD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC,GAAG,WAAW,GAAG,IAAI,CAOrB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,mBAAmB,EAAE,EAC3B,gBAAgB,CAAC,EAAE,MAAM,GACxB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,EAAE,CAAA;CAAE,GAAG,IAAI,CAgB/C"}
|