@songsid/agend 2.0.5-beta.2 → 2.0.5-beta.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/backend/claude-code.js +1 -3
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/cli.js +142 -0
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +3 -0
- package/dist/daemon.js +35 -4
- package/dist/daemon.js.map +1 -1
- package/dist/export-import.js +1 -1
- package/dist/export-import.js.map +1 -1
- package/dist/fleet-manager.js +67 -27
- package/dist/fleet-manager.js.map +1 -1
- package/dist/hang-detector.js +6 -6
- package/dist/hang-detector.js.map +1 -1
- package/dist/instance-lifecycle.js +15 -0
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/outbound-handlers.js +6 -1
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/scheduler/db.d.ts +2 -0
- package/dist/scheduler/db.js +5 -0
- package/dist/scheduler/db.js.map +1 -1
- package/dist/topic-commands.js +6 -26
- package/dist/topic-commands.js.map +1 -1
- package/package.json +1 -1
package/dist/fleet-manager.js
CHANGED
|
@@ -539,9 +539,9 @@ export class FleetManager {
|
|
|
539
539
|
// Snapshotted at startup — new decisions via post_decision are available
|
|
540
540
|
// through list_decisions tool but not auto-injected until restart.
|
|
541
541
|
try {
|
|
542
|
-
const decisions = this.scheduler.db.
|
|
542
|
+
const decisions = this.scheduler.db.listAllActiveDecisions();
|
|
543
543
|
if (decisions.length > 0) {
|
|
544
|
-
const capped = decisions.slice(0, 20).map(d => ({ title: d.title, content: (d.content ?? "").slice(0, 200) }));
|
|
544
|
+
const capped = decisions.slice(0, 20).map(d => ({ title: d.title, content: (d.content ?? "").slice(0, 200), scope: d.scope, project_root: d.project_root }));
|
|
545
545
|
process.env.AGEND_DECISIONS = JSON.stringify(capped);
|
|
546
546
|
this.logger.info({ count: decisions.length, injected: capped.length }, "Injected active decisions into env");
|
|
547
547
|
}
|
|
@@ -780,7 +780,7 @@ export class FleetManager {
|
|
|
780
780
|
timestamp: new Date(),
|
|
781
781
|
});
|
|
782
782
|
}
|
|
783
|
-
else if (data.command === "
|
|
783
|
+
else if (data.command === "save" || data.command === "load") {
|
|
784
784
|
if (!this.classicChannels?.isAdmin(data.userId)) {
|
|
785
785
|
await data.respond("⛔ This command requires admin access.");
|
|
786
786
|
return;
|
|
@@ -791,10 +791,7 @@ export class FleetManager {
|
|
|
791
791
|
return;
|
|
792
792
|
}
|
|
793
793
|
let rawCmd;
|
|
794
|
-
if (data.command === "
|
|
795
|
-
rawCmd = "/compact";
|
|
796
|
-
}
|
|
797
|
-
else if (data.command === "save") {
|
|
794
|
+
if (data.command === "save") {
|
|
798
795
|
const filename = data.options?.filename;
|
|
799
796
|
if (!/^[\w.-]+$/.test(filename)) {
|
|
800
797
|
await data.respond("⛔ Invalid filename — only letters, numbers, dots, hyphens, underscores allowed.");
|
|
@@ -813,6 +810,15 @@ export class FleetManager {
|
|
|
813
810
|
this.pasteRawToClassicInstance(target.name, rawCmd);
|
|
814
811
|
await data.respond(`✅ Sent \`${rawCmd}\` to ${target.name}`);
|
|
815
812
|
}
|
|
813
|
+
else if (data.command === "compact") {
|
|
814
|
+
const target = this.routing.resolve(data.channelId);
|
|
815
|
+
if (!target) {
|
|
816
|
+
await data.respond("No active agent in this channel.");
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
const result = await this.topicCommands.sendCompact(target.name);
|
|
820
|
+
await data.respond(result);
|
|
821
|
+
}
|
|
816
822
|
else if (data.command === "ctx") {
|
|
817
823
|
const target = this.routing.resolve(data.channelId);
|
|
818
824
|
if (!target) {
|
|
@@ -1059,7 +1065,7 @@ export class FleetManager {
|
|
|
1059
1065
|
timestamp: new Date(),
|
|
1060
1066
|
});
|
|
1061
1067
|
}
|
|
1062
|
-
else if (data.command === "
|
|
1068
|
+
else if (data.command === "save" || data.command === "load") {
|
|
1063
1069
|
if (!this.classicChannels?.isAdmin(data.userId)) {
|
|
1064
1070
|
await data.respond("⛔ This command requires admin access.");
|
|
1065
1071
|
return;
|
|
@@ -1070,10 +1076,7 @@ export class FleetManager {
|
|
|
1070
1076
|
return;
|
|
1071
1077
|
}
|
|
1072
1078
|
let rawCmd;
|
|
1073
|
-
if (data.command === "
|
|
1074
|
-
rawCmd = "/compact";
|
|
1075
|
-
}
|
|
1076
|
-
else if (data.command === "save") {
|
|
1079
|
+
if (data.command === "save") {
|
|
1077
1080
|
const filename = data.options?.filename;
|
|
1078
1081
|
if (!/^[\w.-]+$/.test(filename)) {
|
|
1079
1082
|
await data.respond("⛔ Invalid filename — only letters, numbers, dots, hyphens, underscores allowed.");
|
|
@@ -1092,6 +1095,15 @@ export class FleetManager {
|
|
|
1092
1095
|
this.pasteRawToClassicInstance(target.name, rawCmd);
|
|
1093
1096
|
await data.respond(`✅ Sent \`${rawCmd}\` to ${target.name}`);
|
|
1094
1097
|
}
|
|
1098
|
+
else if (data.command === "compact") {
|
|
1099
|
+
const target = this.routing.resolve(data.channelId);
|
|
1100
|
+
if (!target) {
|
|
1101
|
+
await data.respond("No active agent in this channel.");
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
const result = await this.topicCommands.sendCompact(target.name);
|
|
1105
|
+
await data.respond(result);
|
|
1106
|
+
}
|
|
1095
1107
|
else if (data.command === "ctx") {
|
|
1096
1108
|
const target = this.routing.resolve(data.channelId);
|
|
1097
1109
|
if (!target) {
|
|
@@ -1486,6 +1498,10 @@ export class FleetManager {
|
|
|
1486
1498
|
if (text === "/start" || text.startsWith("/start ")) {
|
|
1487
1499
|
if (isPrivateChat) {
|
|
1488
1500
|
if (!this.classicChannels.isUserAllowed(msg.userId)) {
|
|
1501
|
+
const generalId = this.findGeneralInstance(msg.adapterId);
|
|
1502
|
+
if (generalId) {
|
|
1503
|
+
this.notifyInstanceTopic(generalId, `🆕 Unauthorized user tried /start in private chat:\n• Name: ${msg.username}\n• ID: ${msg.userId}\n• Platform: ${msg.source}\n\nTo allow: add \`${msg.userId}\` to classicBot.yaml \`allowed_users\``);
|
|
1504
|
+
}
|
|
1489
1505
|
await msgAdapter?.sendText(chatId, "⛔ You are not in the allowed users list.");
|
|
1490
1506
|
return;
|
|
1491
1507
|
}
|
|
@@ -1547,14 +1563,24 @@ export class FleetManager {
|
|
|
1547
1563
|
await msgAdapter?.sendText(chatId, result);
|
|
1548
1564
|
return;
|
|
1549
1565
|
}
|
|
1566
|
+
// Handle /ctx command
|
|
1567
|
+
if (text === "/ctx" || text.startsWith("/ctx@")) {
|
|
1568
|
+
const ctxTarget = this.routing.resolve(chatId);
|
|
1569
|
+
if (!ctxTarget || ctxTarget.kind !== "classic") {
|
|
1570
|
+
await msgAdapter?.sendText(chatId, "No active agent. Use /start first.");
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
const reply = await this.topicCommands.getCtxText(ctxTarget.name);
|
|
1574
|
+
await msgAdapter?.sendText(chatId, reply);
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1550
1577
|
// Route to classic channel if registered
|
|
1551
1578
|
const target = this.routing.resolve(chatId);
|
|
1552
1579
|
if (target?.kind === "classic") {
|
|
1553
1580
|
if (msg.adapterId)
|
|
1554
1581
|
this.bindInstanceAdapter(target.name, msg.adapterId, true);
|
|
1555
|
-
// TG ClassicBot:
|
|
1556
|
-
|
|
1557
|
-
if (!isBotMentioned) {
|
|
1582
|
+
// TG ClassicBot: group requires @mention, private chat forwards directly.
|
|
1583
|
+
if (!isPrivateChat && !isBotMentioned) {
|
|
1558
1584
|
// No trigger: save attachments + react, log, but don't forward to agent
|
|
1559
1585
|
const syntheticMsg = { ...msg, threadId: chatId, text: rawText.startsWith("/") ? "" : rawText };
|
|
1560
1586
|
await this.handleClassicChannelMessage(target.name, syntheticMsg);
|
|
@@ -1754,7 +1780,7 @@ export class FleetManager {
|
|
|
1754
1780
|
const routingConfig = senderInstanceName
|
|
1755
1781
|
? this.fleetConfig?.instances[senderInstanceName]
|
|
1756
1782
|
: (senderSessionName ? undefined : this.fleetConfig?.instances[instanceName]);
|
|
1757
|
-
|
|
1783
|
+
let threadId = resolveReplyThreadId(args.thread_id, routingConfig)
|
|
1758
1784
|
?? this.classicChannels?.getChannelIdByInstance(senderInstanceName ?? instanceName);
|
|
1759
1785
|
// Select adapter: use instance binding, or resolve from chatId in args
|
|
1760
1786
|
const outAdapter = this.getAdapterForInstance(senderInstanceName ?? instanceName) ?? this.adapter;
|
|
@@ -1762,6 +1788,15 @@ export class FleetManager {
|
|
|
1762
1788
|
respond(null, "No adapter available");
|
|
1763
1789
|
return;
|
|
1764
1790
|
}
|
|
1791
|
+
// For classic instances: clear thread_id to prevent TG interpreting it as message_thread_id
|
|
1792
|
+
// (classic chats — private or group — don't use forum threads)
|
|
1793
|
+
const classicChannelId = this.classicChannels?.getChannelIdByInstance(senderInstanceName ?? instanceName);
|
|
1794
|
+
if (classicChannelId) {
|
|
1795
|
+
if (!args.chat_id)
|
|
1796
|
+
args.chat_id = classicChannelId;
|
|
1797
|
+
delete args.thread_id;
|
|
1798
|
+
threadId = undefined;
|
|
1799
|
+
}
|
|
1765
1800
|
// Route standard channel tools (reply, react, edit_message, download_attachment)
|
|
1766
1801
|
if (routeToolCall(outAdapter, tool, args, threadId, respond)) {
|
|
1767
1802
|
if (tool === "reply") {
|
|
@@ -2799,6 +2834,14 @@ When users create specialized instances, suggest these configurations:
|
|
|
2799
2834
|
const text = msg.text ?? "";
|
|
2800
2835
|
const channelId = msg.threadId ?? msg.chatId;
|
|
2801
2836
|
const isCollabMode = this.classicChannels?.isCollab(channelId) ?? false;
|
|
2837
|
+
// Handle /ctx in classic mode — always, regardless of collab mode
|
|
2838
|
+
if (text === "/ctx" || text.startsWith("/ctx@")) {
|
|
2839
|
+
const reply = await this.topicCommands.getCtxText(instanceName);
|
|
2840
|
+
const classicAdapter = this.worlds.get(msg.adapterId ?? "")?.adapter ?? this.adapter;
|
|
2841
|
+
if (classicAdapter)
|
|
2842
|
+
await classicAdapter.sendText(msg.threadId ?? msg.chatId, reply, { threadId: msg.threadId });
|
|
2843
|
+
return;
|
|
2844
|
+
}
|
|
2802
2845
|
// Collab mode: trigger on @mention of our bot, log all messages
|
|
2803
2846
|
if (isCollabMode) {
|
|
2804
2847
|
// Skip empty bot messages (e.g., reactions) — don't pollute chat log
|
|
@@ -2883,14 +2926,6 @@ When users create specialized instances, suggest these configurations:
|
|
|
2883
2926
|
await this.forwardToClassicInstance(instanceName, finalText, msg, extraMeta);
|
|
2884
2927
|
return;
|
|
2885
2928
|
}
|
|
2886
|
-
// Handle /ctx in classic mode
|
|
2887
|
-
if (text === "/ctx" || text.startsWith("/ctx@")) {
|
|
2888
|
-
const reply = await this.topicCommands.getCtxText(instanceName);
|
|
2889
|
-
const classicAdapter = this.worlds.get(msg.adapterId ?? "")?.adapter ?? this.adapter;
|
|
2890
|
-
if (classicAdapter)
|
|
2891
|
-
await classicAdapter.sendText(msg.threadId ?? msg.chatId, reply, { threadId: msg.threadId });
|
|
2892
|
-
return;
|
|
2893
|
-
}
|
|
2894
2929
|
// Normal mode: /chat trigger
|
|
2895
2930
|
const isChat = text.startsWith("/chat ") || text === "/chat";
|
|
2896
2931
|
this.logger.info({ instanceName, user: msg.username, textLen: text.length, hasChat: isChat }, "classic channel message received");
|
|
@@ -3077,8 +3112,13 @@ When users create specialized instances, suggest these configurations:
|
|
|
3077
3112
|
async handleClassicStart(channelId, channelName, userId, guildId) {
|
|
3078
3113
|
if (!this.classicChannels)
|
|
3079
3114
|
return "Classic channel manager not initialized.";
|
|
3080
|
-
if (guildId && !this.classicChannels.isGuildAllowed(guildId))
|
|
3115
|
+
if (guildId && !this.classicChannels.isGuildAllowed(guildId)) {
|
|
3116
|
+
const generalId = this.findGeneralInstance();
|
|
3117
|
+
if (generalId) {
|
|
3118
|
+
this.notifyInstanceTopic(generalId, `🆕 Unauthorized guild tried /start:\n• Guild ID: ${guildId}\n• User: ${userId}\n• Platform: discord\n\nTo allow: add \`${guildId}\` to classicBot.yaml \`allowed_guilds\``);
|
|
3119
|
+
}
|
|
3081
3120
|
return "⛔ This server is not in the allowed guilds list.";
|
|
3121
|
+
}
|
|
3082
3122
|
if (this.classicChannels.isClassicChannel(channelId))
|
|
3083
3123
|
return "This channel already has an active agent. Use /chat to talk.";
|
|
3084
3124
|
if (this.routing.resolve(channelId))
|
|
@@ -3088,8 +3128,8 @@ When users create specialized instances, suggest these configurations:
|
|
|
3088
3128
|
this.routing.register(channelId, { kind: "classic", name: instanceName });
|
|
3089
3129
|
await this.startClassicInstance(instanceName, this.classicChannels.getBackend(channelId, this.fleetConfig?.defaults?.backend), this.classicChannels.getPreTaskCommand(channelId), this.classicChannels.getModel(channelId, this.fleetConfig?.defaults?.model));
|
|
3090
3130
|
this.reregisterClassicChannels();
|
|
3091
|
-
// Auto-enable collab for Discord classic channels
|
|
3092
|
-
if (!this.classicChannels.isCollab(channelId)) {
|
|
3131
|
+
// Auto-enable collab for Discord classic channels (TG uses @mention directly without collab mode)
|
|
3132
|
+
if (guildId && !this.classicChannels.isCollab(channelId)) {
|
|
3093
3133
|
this.classicChannels.toggleCollab(channelId);
|
|
3094
3134
|
}
|
|
3095
3135
|
this.logger.info({ channelId, instanceName, userId }, "Classic channel started");
|