@songsid/agend 2.1.1-beta.19 → 2.1.1-beta.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/backend/grok.d.ts +23 -0
- package/dist/backend/grok.js +25 -0
- package/dist/backend/grok.js.map +1 -1
- package/dist/daemon.d.ts +10 -0
- package/dist/daemon.js +29 -16
- package/dist/daemon.js.map +1 -1
- package/dist/event-log.d.ts +31 -0
- package/dist/event-log.js +92 -0
- package/dist/event-log.js.map +1 -1
- package/dist/fleet-manager.d.ts +17 -9
- package/dist/fleet-manager.js +35 -40
- package/dist/fleet-manager.js.map +1 -1
- package/dist/process-memory.d.ts +31 -0
- package/dist/process-memory.js +79 -0
- package/dist/process-memory.js.map +1 -0
- package/dist/quickstart.js +17 -16
- package/dist/quickstart.js.map +1 -1
- package/dist/secret-file.d.ts +33 -0
- package/dist/secret-file.js +36 -0
- package/dist/secret-file.js.map +1 -0
- package/dist/setup-wizard.js +9 -7
- package/dist/setup-wizard.js.map +1 -1
- package/dist/tmux-manager.d.ts +17 -1
- package/dist/tmux-manager.js +20 -5
- package/dist/tmux-manager.js.map +1 -1
- package/package.json +1 -1
package/dist/event-log.js
CHANGED
|
@@ -29,6 +29,24 @@ export class EventLog {
|
|
|
29
29
|
CREATE INDEX IF NOT EXISTS idx_events_type ON events(event_type, created_at);
|
|
30
30
|
`);
|
|
31
31
|
this.insertStmt = this.db.prepare("INSERT INTO events (instance_name, event_type, payload) VALUES (?, ?, ?)");
|
|
32
|
+
// Reaction queue (#432 rework of #408/#413). A reaction is context, not a
|
|
33
|
+
// message: it no longer triggers an agent turn. It waits here until the next
|
|
34
|
+
// REAL message to the same instance, rides in as one compact context line,
|
|
35
|
+
// and is then marked consumed. consumed_at stays (rather than DELETE) so a
|
|
36
|
+
// delivery that fails after summarising does not lose the reactions — they
|
|
37
|
+
// are only marked once the message actually went out.
|
|
38
|
+
this.db.exec(`
|
|
39
|
+
CREATE TABLE IF NOT EXISTS reactions (
|
|
40
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
41
|
+
instance_name TEXT NOT NULL,
|
|
42
|
+
message_id TEXT NOT NULL,
|
|
43
|
+
username TEXT NOT NULL,
|
|
44
|
+
emoji TEXT NOT NULL,
|
|
45
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
46
|
+
consumed_at TEXT
|
|
47
|
+
);
|
|
48
|
+
CREATE INDEX IF NOT EXISTS idx_reactions_pending ON reactions(instance_name, consumed_at);
|
|
49
|
+
`);
|
|
32
50
|
// Activity log for visualization
|
|
33
51
|
this.db.exec(`
|
|
34
52
|
CREATE TABLE IF NOT EXISTS activity (
|
|
@@ -96,6 +114,80 @@ export class EventLog {
|
|
|
96
114
|
this.db
|
|
97
115
|
.prepare("DELETE FROM activity WHERE timestamp < datetime('now', ?)")
|
|
98
116
|
.run(`-${days} days`);
|
|
117
|
+
// Reactions have their own, shorter horizon: after REACTION_RETENTION_DAYS a
|
|
118
|
+
// pending reaction is stale context nobody should be shown, consumed or not.
|
|
119
|
+
this.db
|
|
120
|
+
.prepare("DELETE FROM reactions WHERE created_at < datetime('now', ?)")
|
|
121
|
+
.run(`-${EventLog.REACTION_RETENTION_DAYS} days`);
|
|
122
|
+
}
|
|
123
|
+
static REACTION_RETENTION_DAYS = 7;
|
|
124
|
+
/** Queue a reaction for the instance's next real message. */
|
|
125
|
+
addReaction(instance, messageId, username, emoji) {
|
|
126
|
+
this.db
|
|
127
|
+
.prepare("INSERT INTO reactions (instance_name, message_id, username, emoji) VALUES (?, ?, ?, ?)")
|
|
128
|
+
.run(instance, messageId, username, emoji);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* A withdrawn reaction is withdrawn context. Deleting the matching pending row
|
|
132
|
+
* (newest first, one per removal) means an add-then-remove nets out to nothing,
|
|
133
|
+
* instead of reporting a 👍 the user visibly took back. Already-consumed rows
|
|
134
|
+
* are left alone — the agent has seen those; history does not un-happen.
|
|
135
|
+
*/
|
|
136
|
+
removeReaction(instance, messageId, username, emoji) {
|
|
137
|
+
this.db
|
|
138
|
+
.prepare(`DELETE FROM reactions WHERE id = (
|
|
139
|
+
SELECT id FROM reactions
|
|
140
|
+
WHERE instance_name = ? AND message_id = ? AND username = ? AND emoji = ? AND consumed_at IS NULL
|
|
141
|
+
ORDER BY id DESC LIMIT 1
|
|
142
|
+
)`)
|
|
143
|
+
.run(instance, messageId, username, emoji);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Pending reactions as compact context, grouped by the message they landed on:
|
|
147
|
+
*
|
|
148
|
+
* msg 1532964272244523028: 👍×2 from hanhanv, ❓ from user2 | msg 1532965…: 👍 from hanhanv
|
|
149
|
+
*
|
|
150
|
+
* The message id is the point (#433): the agent's inbound blocks render each of
|
|
151
|
+
* its own messages' ids, so carrying the id is what lets it tell WHICH of its
|
|
152
|
+
* messages was reacted to. Within a message, duplicates of the same emoji from
|
|
153
|
+
* the same user collapse to ×N (a re-added reaction is not new information).
|
|
154
|
+
*
|
|
155
|
+
* Returns null (not an empty string) when there is nothing pending, so callers
|
|
156
|
+
* add zero context in the common case. `maxId` is what the caller hands back to
|
|
157
|
+
* markReactionsConsumed — bounding by id keeps a reaction that arrives DURING
|
|
158
|
+
* delivery pending for the next message instead of being silently eaten.
|
|
159
|
+
*/
|
|
160
|
+
pendingReactions(instance) {
|
|
161
|
+
const rows = this.db
|
|
162
|
+
.prepare("SELECT id, message_id, username, emoji FROM reactions WHERE instance_name = ? AND consumed_at IS NULL ORDER BY id")
|
|
163
|
+
.all(instance);
|
|
164
|
+
if (rows.length === 0)
|
|
165
|
+
return null;
|
|
166
|
+
// message_id → (emoji+user → count), both insertion-ordered so the summary
|
|
167
|
+
// reads in arrival order.
|
|
168
|
+
const byMessage = new Map();
|
|
169
|
+
for (const row of rows) {
|
|
170
|
+
const counts = byMessage.get(row.message_id) ?? new Map();
|
|
171
|
+
const key = `${row.emoji}\u0000${row.username}`;
|
|
172
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
173
|
+
byMessage.set(row.message_id, counts);
|
|
174
|
+
}
|
|
175
|
+
const groups = [];
|
|
176
|
+
for (const [messageId, counts] of byMessage) {
|
|
177
|
+
const parts = [];
|
|
178
|
+
for (const [key, count] of counts) {
|
|
179
|
+
const [emoji, username] = key.split("\u0000");
|
|
180
|
+
parts.push(count > 1 ? `${emoji}×${count} from ${username}` : `${emoji} from ${username}`);
|
|
181
|
+
}
|
|
182
|
+
groups.push(`msg ${messageId}: ${parts.join(", ")}`);
|
|
183
|
+
}
|
|
184
|
+
return { summary: groups.join(" | "), maxId: rows[rows.length - 1].id };
|
|
185
|
+
}
|
|
186
|
+
/** Mark reactions up to `maxId` as shown. Call only after the delivery succeeded. */
|
|
187
|
+
markReactionsConsumed(instance, maxId) {
|
|
188
|
+
this.db
|
|
189
|
+
.prepare("UPDATE reactions SET consumed_at = datetime('now') WHERE instance_name = ? AND id <= ? AND consumed_at IS NULL")
|
|
190
|
+
.run(instance, maxId);
|
|
99
191
|
}
|
|
100
192
|
close() {
|
|
101
193
|
this.db.close();
|
package/dist/event-log.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-log.js","sourceRoot":"","sources":["../src/event-log.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"event-log.js","sourceRoot":"","sources":["../src/event-log.ts"],"names":[],"mappings":"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"}
|
package/dist/fleet-manager.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FleetMemory } from "./process-memory.js";
|
|
1
2
|
import type { FleetConfig, RawFleetConfig, InstanceConfig } from "./types.js";
|
|
2
3
|
import { type RouteTarget } from "./fleet-context.js";
|
|
3
4
|
import { EventLog } from "./event-log.js";
|
|
@@ -308,6 +309,10 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
308
309
|
states: Record<string, string>;
|
|
309
310
|
};
|
|
310
311
|
startupComplete: boolean;
|
|
312
|
+
/** See process-memory.ts: the fleet process and the whole service cgroup are
|
|
313
|
+
* reported separately because they differ by ~60x and only one of them can
|
|
314
|
+
* show a fleet-manager leak. */
|
|
315
|
+
memory: FleetMemory;
|
|
311
316
|
problems: string[];
|
|
312
317
|
};
|
|
313
318
|
/** Start the primary adapter (backward-compatible, sets this.adapter) */
|
|
@@ -327,17 +332,20 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
327
332
|
/**
|
|
328
333
|
* A user reacted to one of the bot's messages (#408).
|
|
329
334
|
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
* Policy: only the approval emojis wake an instance. A 👍 costing a full agent
|
|
336
|
-
* turn is acceptable when it means "approved"; every other emoji is chatter and
|
|
337
|
-
* must not spend a turn, so those are delivered only to an instance that is
|
|
338
|
-
* already awake (`no_wake`) and dropped for a paused one.
|
|
335
|
+
* A reaction is context, not a message (#432, reworking #413): it never triggers
|
|
336
|
+
* an agent turn and never wakes anything. It is queued in the event log and rides
|
|
337
|
+
* into the instance's NEXT real message as one compact leading line —
|
|
338
|
+
* `[Recent reactions: 👍×2 from hanhanv]` — after which it is marked consumed.
|
|
339
|
+
* No pending reactions → no line → zero context spent, which is the common case.
|
|
339
340
|
*/
|
|
340
341
|
private handleInboundReaction;
|
|
342
|
+
/**
|
|
343
|
+
* The queued-reaction summary for an instance's next real message, or {} when
|
|
344
|
+
* nothing is pending (the common case must add zero context). The consume
|
|
345
|
+
* callback is separate from the fetch so reactions are only marked once the
|
|
346
|
+
* message actually went out — a failed delivery keeps them queued.
|
|
347
|
+
*/
|
|
348
|
+
private pendingReactionsMeta;
|
|
341
349
|
private handleInboundMessage;
|
|
342
350
|
/** Handle outbound tool calls from a daemon instance */
|
|
343
351
|
/** Warn (but don't block) when rate limits are high. 30-min debounce per instance. */
|
package/dist/fleet-manager.js
CHANGED
|
@@ -5,6 +5,7 @@ import { join, dirname, basename } from "node:path";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { getAgendHome, ensureWorkspaceGit } from "./paths.js";
|
|
7
7
|
import { sdNotify, sdNotifyBlocking } from "./sd-notify.js";
|
|
8
|
+
import { readFleetMemory } from "./process-memory.js";
|
|
8
9
|
import { isScalar, parseDocument } from "yaml";
|
|
9
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
11
|
const __dirname = dirname(__filename);
|
|
@@ -105,12 +106,6 @@ const PROGRESS_ACTIVITY_MAX_CHARS = 48;
|
|
|
105
106
|
*/
|
|
106
107
|
const IPC_RECONNECT_GRACE_MS = 30_000;
|
|
107
108
|
const IPC_RECONNECT_POLL_MS = 250;
|
|
108
|
-
/**
|
|
109
|
-
* Reactions that count as an explicit approve/reject signal, and are therefore worth
|
|
110
|
-
* waking a paused instance for. Everything else is chatter: delivered only if the
|
|
111
|
-
* instance is already awake, never worth a wake-up plus a full agent turn.
|
|
112
|
-
*/
|
|
113
|
-
const REACTION_APPROVAL_EMOJIS = new Set(["👍", "👎"]);
|
|
114
109
|
const CLASSIC_BACKEND_SELECTION_TIMEOUT_MS = 60_000;
|
|
115
110
|
const CLASSIC_BACKEND_CALLBACK_PREFIX = "classic-backend:";
|
|
116
111
|
const MODEL_SELECT_CALLBACK_PREFIX = "model-select:";
|
|
@@ -1569,6 +1564,7 @@ export class FleetManager {
|
|
|
1569
1564
|
instances: counts,
|
|
1570
1565
|
adapters: { total: this.adapterState.size, connected, states },
|
|
1571
1566
|
startupComplete: this.startupComplete,
|
|
1567
|
+
memory: readFleetMemory(),
|
|
1572
1568
|
problems,
|
|
1573
1569
|
};
|
|
1574
1570
|
}
|
|
@@ -2383,15 +2379,11 @@ export class FleetManager {
|
|
|
2383
2379
|
/**
|
|
2384
2380
|
* A user reacted to one of the bot's messages (#408).
|
|
2385
2381
|
*
|
|
2386
|
-
*
|
|
2387
|
-
*
|
|
2388
|
-
*
|
|
2389
|
-
*
|
|
2390
|
-
*
|
|
2391
|
-
* Policy: only the approval emojis wake an instance. A 👍 costing a full agent
|
|
2392
|
-
* turn is acceptable when it means "approved"; every other emoji is chatter and
|
|
2393
|
-
* must not spend a turn, so those are delivered only to an instance that is
|
|
2394
|
-
* already awake (`no_wake`) and dropped for a paused one.
|
|
2382
|
+
* A reaction is context, not a message (#432, reworking #413): it never triggers
|
|
2383
|
+
* an agent turn and never wakes anything. It is queued in the event log and rides
|
|
2384
|
+
* into the instance's NEXT real message as one compact leading line —
|
|
2385
|
+
* `[Recent reactions: 👍×2 from hanhanv]` — after which it is marked consumed.
|
|
2386
|
+
* No pending reactions → no line → zero context spent, which is the common case.
|
|
2395
2387
|
*/
|
|
2396
2388
|
async handleInboundReaction(r) {
|
|
2397
2389
|
const instanceName = this.resolveSlashTarget(r.threadId ?? r.chatId, r.adapterId);
|
|
@@ -2399,35 +2391,30 @@ export class FleetManager {
|
|
|
2399
2391
|
this.logger.debug({ emoji: r.emoji, chatId: r.chatId }, "Reaction in an unrouted channel — ignoring");
|
|
2400
2392
|
return;
|
|
2401
2393
|
}
|
|
2402
|
-
const isApproval = REACTION_APPROVAL_EMOJIS.has(r.emoji);
|
|
2403
|
-
const verb = r.action === "add" ? "reacted" : "removed their reaction";
|
|
2404
|
-
const content = `[reaction:${r.emoji}] ${r.username} ${verb} on your message ${r.messageId}`;
|
|
2405
2394
|
this.eventLog?.logActivity("reaction", r.username, `${r.emoji} ${r.action}`, instanceName);
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
type: "fleet_inbound",
|
|
2409
|
-
content,
|
|
2410
|
-
meta: {
|
|
2411
|
-
chat_id: "", // not a chat message — must not become the reply target
|
|
2412
|
-
thread_id: "",
|
|
2413
|
-
message_id: r.messageId,
|
|
2414
|
-
user: r.username,
|
|
2415
|
-
user_id: r.userId,
|
|
2416
|
-
source: r.source,
|
|
2417
|
-
ts: r.timestamp.toISOString(),
|
|
2418
|
-
request_kind: "update",
|
|
2419
|
-
requires_reply: "false",
|
|
2420
|
-
// Non-approval reactions never wake a paused instance.
|
|
2421
|
-
...(isApproval ? {} : { no_wake: "true" }),
|
|
2422
|
-
},
|
|
2423
|
-
});
|
|
2395
|
+
if (r.action === "add") {
|
|
2396
|
+
this.eventLog?.addReaction(instanceName, r.messageId, r.username, r.emoji);
|
|
2424
2397
|
}
|
|
2425
|
-
|
|
2426
|
-
//
|
|
2427
|
-
|
|
2428
|
-
this.logger.debug({ err, instanceName, emoji: r.emoji }, "Reaction delivery failed");
|
|
2398
|
+
else {
|
|
2399
|
+
// Withdrawn before anyone saw it → it never happened. See removeReaction.
|
|
2400
|
+
this.eventLog?.removeReaction(instanceName, r.messageId, r.username, r.emoji);
|
|
2429
2401
|
}
|
|
2430
2402
|
}
|
|
2403
|
+
/**
|
|
2404
|
+
* The queued-reaction summary for an instance's next real message, or {} when
|
|
2405
|
+
* nothing is pending (the common case must add zero context). The consume
|
|
2406
|
+
* callback is separate from the fetch so reactions are only marked once the
|
|
2407
|
+
* message actually went out — a failed delivery keeps them queued.
|
|
2408
|
+
*/
|
|
2409
|
+
pendingReactionsMeta(instanceName) {
|
|
2410
|
+
const pending = this.eventLog?.pendingReactions(instanceName);
|
|
2411
|
+
if (!pending)
|
|
2412
|
+
return { meta: {}, consume: () => { } };
|
|
2413
|
+
return {
|
|
2414
|
+
meta: { pending_reactions: pending.summary },
|
|
2415
|
+
consume: () => this.eventLog?.markReactionsConsumed(instanceName, pending.maxId),
|
|
2416
|
+
};
|
|
2417
|
+
}
|
|
2431
2418
|
async handleInboundMessage(msg) {
|
|
2432
2419
|
const threadId = msg.threadId || undefined;
|
|
2433
2420
|
this.logger.debug({ source: msg.source, chatId: msg.chatId, threadId, userId: msg.userId, isBotMessage: msg.isBotMessage, textLen: (msg.text ?? "").length, text: (msg.text ?? "").slice(0, 80) }, "handleInboundMessage entry");
|
|
@@ -2765,6 +2752,7 @@ export class FleetManager {
|
|
|
2765
2752
|
}
|
|
2766
2753
|
this.warnIfRateLimited(generalInstance, msg);
|
|
2767
2754
|
const { text, extraMeta } = await processAttachments(msg, inboundAdapter, this.logger, generalInstance);
|
|
2755
|
+
const generalReactions = this.pendingReactionsMeta(generalInstance);
|
|
2768
2756
|
try {
|
|
2769
2757
|
await this.deliverToInstance(generalInstance, {
|
|
2770
2758
|
type: "fleet_inbound",
|
|
@@ -2780,9 +2768,11 @@ export class FleetManager {
|
|
|
2780
2768
|
adapter_id: msg.adapterId,
|
|
2781
2769
|
source: msg.source,
|
|
2782
2770
|
...(msg.replyToText ? { reply_to_text: msg.replyToText } : {}),
|
|
2771
|
+
...generalReactions.meta,
|
|
2783
2772
|
...extraMeta,
|
|
2784
2773
|
},
|
|
2785
2774
|
});
|
|
2775
|
+
generalReactions.consume();
|
|
2786
2776
|
this.lastInboundUser.set(generalInstance, msg.username);
|
|
2787
2777
|
this.logger.info(`${msg.username} → ${generalInstance}: ${(text ?? "").slice(0, 100)}`);
|
|
2788
2778
|
this.eventLog?.logActivity("message", msg.username, (text ?? "").slice(0, 200), generalInstance);
|
|
@@ -2858,6 +2848,7 @@ export class FleetManager {
|
|
|
2858
2848
|
this.setTopicIcon(instanceName, "blue");
|
|
2859
2849
|
this.warnIfRateLimited(instanceName, msg);
|
|
2860
2850
|
const { text, extraMeta } = await processAttachments(msg, inboundAdapter, this.logger, instanceName);
|
|
2851
|
+
const reactions = this.pendingReactionsMeta(instanceName);
|
|
2861
2852
|
try {
|
|
2862
2853
|
await this.deliverToInstance(instanceName, {
|
|
2863
2854
|
type: "fleet_inbound",
|
|
@@ -2873,9 +2864,13 @@ export class FleetManager {
|
|
|
2873
2864
|
adapter_id: msg.adapterId,
|
|
2874
2865
|
source: msg.source,
|
|
2875
2866
|
...(msg.replyToText ? { reply_to_text: msg.replyToText } : {}),
|
|
2867
|
+
...reactions.meta,
|
|
2876
2868
|
...extraMeta,
|
|
2877
2869
|
},
|
|
2878
2870
|
});
|
|
2871
|
+
// Only after the message actually went out. A failed delivery keeps the
|
|
2872
|
+
// reactions queued for the retry / the next message.
|
|
2873
|
+
reactions.consume();
|
|
2879
2874
|
}
|
|
2880
2875
|
catch (err) {
|
|
2881
2876
|
this.logger.warn({ err: err.message, instanceName }, "Wake/delivery failed");
|