@songsid/agend 2.1.12-beta.3 โ 2.1.12-beta.5
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 +54 -8
- package/README.zh-TW.md +104 -11
- package/dist/agent-endpoint.d.ts +2 -0
- package/dist/agent-endpoint.js +38 -29
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/backend/factory.js +5 -5
- package/dist/backend/factory.js.map +1 -1
- package/dist/backend/index.d.ts +0 -1
- package/dist/backend/index.js +0 -1
- package/dist/backend/index.js.map +1 -1
- package/dist/backend/kiro.d.ts +10 -1
- package/dist/backend/kiro.js +13 -0
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/mock.d.ts +1 -1
- package/dist/backend/mock.js +1 -1
- package/dist/backend/muse.d.ts +14 -0
- package/dist/backend/muse.js +60 -11
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/removed.d.ts +12 -0
- package/dist/backend/removed.js +18 -0
- package/dist/backend/removed.js.map +1 -0
- package/dist/backend/session-signals.js +5 -10
- package/dist/backend/session-signals.js.map +1 -1
- package/dist/backend/types.d.ts +1 -1
- package/dist/backend/types.js +0 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/adapters/discord.js +9 -0
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +14 -8
- package/dist/channel/adapters/telegram.js +80 -26
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-tools.js +2 -2
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/classic-channel-manager.js +2 -2
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +27 -53
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +1 -1
- package/dist/command-table.js +4 -2
- package/dist/command-table.js.map +1 -1
- package/dist/config-validator.js +82 -10
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +7 -5
- package/dist/config.js.map +1 -1
- package/dist/context-guardian.d.ts +1 -1
- package/dist/context-guardian.js +1 -1
- package/dist/cross-instance-notice.d.ts +39 -0
- package/dist/cross-instance-notice.js +84 -0
- package/dist/cross-instance-notice.js.map +1 -0
- package/dist/daemon.d.ts +6 -0
- package/dist/daemon.js +54 -42
- package/dist/daemon.js.map +1 -1
- package/dist/daily-summary.js +0 -3
- package/dist/daily-summary.js.map +1 -1
- package/dist/fleet-manager.d.ts +16 -3
- package/dist/fleet-manager.js +110 -53
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +1 -1
- package/dist/instance-config-impact.js +3 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +9 -1
- package/dist/instance-lifecycle.js +66 -7
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.js +28 -2
- package/dist/locale.js.map +1 -1
- package/dist/memory-pressure.d.ts +8 -2
- package/dist/memory-pressure.js +10 -4
- package/dist/memory-pressure.js.map +1 -1
- package/dist/outbound-handlers.js +36 -23
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +0 -1
- package/dist/outbound-schemas.js +4 -4
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/prompt-file-ref.d.ts +42 -0
- package/dist/prompt-file-ref.js +118 -0
- package/dist/prompt-file-ref.js.map +1 -0
- package/dist/reply-dedup.d.ts +5 -0
- package/dist/reply-dedup.js +9 -0
- package/dist/reply-dedup.js.map +1 -1
- package/dist/settings-api.js +5 -1
- package/dist/settings-api.js.map +1 -1
- package/dist/setup-wizard.js +0 -4
- package/dist/setup-wizard.js.map +1 -1
- package/dist/spawn-gate.js +4 -3
- package/dist/spawn-gate.js.map +1 -1
- package/dist/target-queue-worker.d.ts +1 -1
- package/dist/topic-commands.d.ts +64 -5
- package/dist/topic-commands.js +172 -14
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-jsonl.d.ts +12 -0
- package/dist/transcript-jsonl.js +37 -0
- package/dist/transcript-jsonl.js.map +1 -1
- package/dist/transcript-monitor.js +10 -7
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +1 -1
- package/dist/transcript-sources.js +29 -11
- package/dist/transcript-sources.js.map +1 -1
- package/dist/types.d.ts +13 -4
- package/dist/ui/dashboard.html +3 -3
- package/dist/ui/settings.html +58 -16
- package/dist/update-check.d.ts +15 -6
- package/dist/update-check.js +20 -7
- package/dist/update-check.js.map +1 -1
- package/dist/wake-coordinator.js +1 -1
- package/dist/web-api.js +1 -2
- package/dist/web-api.js.map +1 -1
- package/package.json +1 -1
- package/dist/backend/gemini-cli.d.ts +0 -20
- package/dist/backend/gemini-cli.js +0 -169
- package/dist/backend/gemini-cli.js.map +0 -1
package/dist/daily-summary.js
CHANGED
|
@@ -36,13 +36,10 @@ export class DailySummary {
|
|
|
36
36
|
const lines = [`๐ Daily Report โ ${today}`, ""];
|
|
37
37
|
for (const name of instances) {
|
|
38
38
|
const instanceEvents = todayEvents.filter(e => e.instance_name === name);
|
|
39
|
-
const restarts = instanceEvents.filter(e => e.event_type === "context_restart").length;
|
|
40
39
|
const hangs = instanceEvents.filter(e => e.event_type === "hang_detected").length;
|
|
41
40
|
const deferred = instanceEvents.filter(e => e.event_type === "schedule_deferred").length;
|
|
42
41
|
const costCents = costCentsMap.get(name) ?? 0;
|
|
43
42
|
let line = `${name}: ${formatCents(costCents)}`;
|
|
44
|
-
if (restarts > 0)
|
|
45
|
-
line += `, ${restarts} restart${restarts > 1 ? "s" : ""}`;
|
|
46
43
|
if (deferred > 0)
|
|
47
44
|
line += `, ${deferred} deferred`;
|
|
48
45
|
const anomalies = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daily-summary.js","sourceRoot":"","sources":["../src/daily-summary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAG9B,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE5E,MAAM,OAAO,YAAY;IAIb;IACA;IACA;IACA;IANF,GAAG,GAAgB,IAAI,CAAC;IAEhC,YACU,MAA0B,EAC1B,QAAgB,EAChB,SAAiC,EACjC,cAA4B;QAH5B,WAAM,GAAN,MAAM,CAAoB;QAC1B,aAAQ,GAAR,QAAQ,CAAQ;QAChB,cAAS,GAAT,SAAS,CAAwB;QACjC,mBAAc,GAAd,cAAc,CAAc;IACnC,CAAC;IAEJ,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO;QACjC,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC;QAC/D,IAAI,CAAC,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE;YAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;YACnC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI;QACF,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;QACjB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;IAClB,CAAC;IAED,MAAM,CAAC,YAAY,CACjB,QAAkB,EAClB,SAAmB,EACnB,YAAiC,EACjC,eAAuB;QAEvB,0EAA0E;QAC1E,8EAA8E;QAC9E,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,KAAK,WAAW,EAAE,EAAE,CAAC,CAAC;QACxD,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAE3D,MAAM,KAAK,GAAa,CAAC,qBAAqB,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QAE3D,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,MAAM,cAAc,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC;YACzE,MAAM,
|
|
1
|
+
{"version":3,"file":"daily-summary.js","sourceRoot":"","sources":["../src/daily-summary.ts"],"names":[],"mappings":"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"}
|
package/dist/fleet-manager.d.ts
CHANGED
|
@@ -51,6 +51,14 @@ export declare function selectLruEvictions(warm: string[], cap: number, opts: {
|
|
|
51
51
|
isIdle: (name: string) => boolean;
|
|
52
52
|
lastInboundAt: (name: string) => number;
|
|
53
53
|
}): string[];
|
|
54
|
+
/**
|
|
55
|
+
* Window-name shape this fleet recognises as an instance window: the
|
|
56
|
+
* `-t<digits>` form every allocator emits, plus legacy `classic-` windows.
|
|
57
|
+
* Startup cleanup reaps windows matching this that are no longer in
|
|
58
|
+
* fleet.yaml. Keep in sync with `uniqueInstanceName` โ every name it emits
|
|
59
|
+
* must match here, or a deleted instance's CLI is left running (#1305 P2-3).
|
|
60
|
+
*/
|
|
61
|
+
export declare function isOrphanInstanceWindowName(name: string): boolean;
|
|
54
62
|
/**
|
|
55
63
|
* Answer shape for `list_models`. `scope` reports where the LIST came from โ
|
|
56
64
|
* "instance" only when it was read through that instance's own backend config,
|
|
@@ -530,6 +538,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
530
538
|
private onFullRestartSignal;
|
|
531
539
|
private handleUpdateSlash;
|
|
532
540
|
private handleRestartSlash;
|
|
541
|
+
/** #1302: `/visibility [mode]`. The dispatcher has already applied the command table's fleet-admin gate. */
|
|
542
|
+
private handleVisibilitySlash;
|
|
533
543
|
private handleTipsSlash;
|
|
534
544
|
/** Admin-only full conversation reset for fleet-topic and Classic instances. */
|
|
535
545
|
private handleClearSlash;
|
|
@@ -1197,7 +1207,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
1197
1207
|
private handleTaskCrud;
|
|
1198
1208
|
/** Create a forum topic via the adapter. Returns the message_thread_id. */
|
|
1199
1209
|
createForumTopic(topicName: string, adapterId?: string): Promise<number | string>;
|
|
1200
|
-
deleteForumTopic(topicId: number | string): Promise<void>;
|
|
1210
|
+
deleteForumTopic(topicId: number | string, adapterId?: string): Promise<void>;
|
|
1211
|
+
getForumTopicDeleter(adapterId?: string): ((topicId: number | string) => Promise<void>) | null;
|
|
1201
1212
|
private topicCleanupTimer;
|
|
1202
1213
|
private sessionPruneTimer;
|
|
1203
1214
|
private classicReloadTimer;
|
|
@@ -1412,6 +1423,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
1412
1423
|
* own adapter rather than through reactMessageStatus (classic bots).
|
|
1413
1424
|
*/
|
|
1414
1425
|
private receivedReactionFor;
|
|
1426
|
+
private reactClassicReceived;
|
|
1427
|
+
private reactClassicForwardedAttachment;
|
|
1415
1428
|
/**
|
|
1416
1429
|
* The stamp for an inbound photo / file a classic bot saved (#1080): the
|
|
1417
1430
|
* instance's `status_emojis.photo|attachment`, then its connection's, then
|
|
@@ -1419,7 +1432,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
1419
1432
|
* Anything else a saved attachment can be counts as a file.
|
|
1420
1433
|
*/
|
|
1421
1434
|
private savedAttachmentReactionFor;
|
|
1422
|
-
reactMessageStatus(instanceName: string, chatId: string, messageId: string, status: DeliveryStatus, threadId?: string): void;
|
|
1435
|
+
reactMessageStatus(instanceName: string, chatId: string, messageId: string, status: DeliveryStatus, threadId?: string, receivedAt?: number): void;
|
|
1423
1436
|
/**
|
|
1424
1437
|
* Apply a terminal delivery verdict. On Telegram the built-in delivered is
|
|
1425
1438
|
* ๐ and failed is ๐; Discord shows โ
/โ โ both configurable (#1005).
|
|
@@ -1968,7 +1981,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
1968
1981
|
/**
|
|
1969
1982
|
* Where each backend natively loads on-demand skills from, relative to the
|
|
1970
1983
|
* workspace. Backends without a native skill mechanism (opencode, grok,
|
|
1971
|
-
* antigravity
|
|
1984
|
+
* antigravity) are deliberately absent: dropping files a CLI
|
|
1972
1985
|
* never reads is clutter, not capability. Unknown directories are ignored
|
|
1973
1986
|
* by older CLI versions, so publishing is fail-open across upgrades.
|
|
1974
1987
|
*/
|
package/dist/fleet-manager.js
CHANGED
|
@@ -14,7 +14,7 @@ import { formatUpdateProgress } from "./update-progress.js";
|
|
|
14
14
|
import { sdNotify, sdNotifyBlocking } from "./sd-notify.js";
|
|
15
15
|
import { readFleetMemory } from "./process-memory.js";
|
|
16
16
|
import { MemoryPressure } from "./memory-pressure.js";
|
|
17
|
-
import { ReplyDeduper } from "./reply-dedup.js";
|
|
17
|
+
import { replyDedupText, ReplyDeduper } from "./reply-dedup.js";
|
|
18
18
|
import { isMap, isScalar, parseDocument } from "yaml";
|
|
19
19
|
const __filename = fileURLToPath(import.meta.url);
|
|
20
20
|
const __dirname = dirname(__filename);
|
|
@@ -64,7 +64,8 @@ import { LoginSession } from "./login-manager.js";
|
|
|
64
64
|
import { tightenInstanceDirs } from "./private-dir.js";
|
|
65
65
|
import { decideSlash } from "./slash-authz.js";
|
|
66
66
|
import { commandSpec, decideCommand } from "./command-table.js";
|
|
67
|
-
import {
|
|
67
|
+
import { runVisibilityCommand } from "./cross-instance-notice.js";
|
|
68
|
+
import { UPDATE_COMMAND, installedChannel, isPrereleaseVersion, updateNoticeKey } from "./update-check.js";
|
|
68
69
|
import { LoginController, LOGIN_TOKEN_RESEND_PREFIX, POST_LOGIN_RECOVERY_DEADLINE_MS } from "./login-controller.js";
|
|
69
70
|
import { runBeforeDeadline } from "./deadline.js";
|
|
70
71
|
import { LoginWindowLock } from "./login-window-lock.js";
|
|
@@ -76,6 +77,7 @@ import { handleAgentRequest, ToolNotPermittedError } from "./agent-endpoint.js";
|
|
|
76
77
|
import { ClassicChannelManager, getClassicBackendChoices, isSelectableClassicBackend, readClassicLastActivityAt } from "./classic-channel-manager.js";
|
|
77
78
|
import { assertExplicitInstanceRemoval } from "./instance-removal.js";
|
|
78
79
|
import { validateFleetConfig } from "./config-validator.js";
|
|
80
|
+
import { isRemovedBackend, removedBackendMessage } from "./backend/removed.js";
|
|
79
81
|
import { presentationState, interactionSummary } from "./interaction-observation.js";
|
|
80
82
|
import { readLastInboundAt } from "./daemon.js";
|
|
81
83
|
import { clearPausedMarker, readPausedAt, readPauseReason, writePausedMarker } from "./pause-marker.js";
|
|
@@ -139,6 +141,16 @@ export function selectLruEvictions(warm, cap, opts) {
|
|
|
139
141
|
candidates.sort((a, b) => opts.lastInboundAt(a) - opts.lastInboundAt(b));
|
|
140
142
|
return candidates.slice(0, warm.length - cap);
|
|
141
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Window-name shape this fleet recognises as an instance window: the
|
|
146
|
+
* `-t<digits>` form every allocator emits, plus legacy `classic-` windows.
|
|
147
|
+
* Startup cleanup reaps windows matching this that are no longer in
|
|
148
|
+
* fleet.yaml. Keep in sync with `uniqueInstanceName` โ every name it emits
|
|
149
|
+
* must match here, or a deleted instance's CLI is left running (#1305 P2-3).
|
|
150
|
+
*/
|
|
151
|
+
export function isOrphanInstanceWindowName(name) {
|
|
152
|
+
return /-t\d+$/.test(name) || /^classic-/.test(name);
|
|
153
|
+
}
|
|
142
154
|
/** Retry cadence for retiring a cancel button whose delete failed (e.g. a DC
|
|
143
155
|
* forum thread the bot momentarily can't reach). 3 retries ร 5min = 15min. */
|
|
144
156
|
const CANCEL_BTN_RETRY_INTERVAL_MS = 5 * 60_000;
|
|
@@ -1007,7 +1019,10 @@ export class FleetManager {
|
|
|
1007
1019
|
this.logger.debug({ hostMemory: snapshot }, "Host memory sample");
|
|
1008
1020
|
}
|
|
1009
1021
|
const changed = snapshot.level !== this.memoryLogLevel;
|
|
1010
|
-
if (this.memoryPressure.
|
|
1022
|
+
if (this.memoryPressure.advisoryOnly()) {
|
|
1023
|
+
// macOS (#1256): the sample is kept in the log for calibration, but nothing is sent to a channel.
|
|
1024
|
+
if (changed)
|
|
1025
|
+
this.logger.info({ hostMemory: snapshot }, "Host memory sample (macOS: logged only โ no notice, no spawn throttling)");
|
|
1011
1026
|
this.memoryLogLevel = snapshot.level;
|
|
1012
1027
|
return;
|
|
1013
1028
|
}
|
|
@@ -1551,6 +1566,13 @@ export class FleetManager {
|
|
|
1551
1566
|
const chatId = String(this.getChannelConfig(adapterId)?.group_id ?? data.channelId);
|
|
1552
1567
|
await this.requestFullRestart(adapter, chatId, data.channelId, messageId);
|
|
1553
1568
|
}
|
|
1569
|
+
/** #1302: `/visibility [mode]`. The dispatcher has already applied the command table's fleet-admin gate. */
|
|
1570
|
+
async handleVisibilitySlash(data) {
|
|
1571
|
+
if (!this.fleetConfig)
|
|
1572
|
+
return;
|
|
1573
|
+
const mode = typeof data.options?.mode === "string" ? data.options.mode : "";
|
|
1574
|
+
await data.respond(runVisibilityCommand(this.fleetConfig, mode, () => this.saveFleetConfig()));
|
|
1575
|
+
}
|
|
1554
1576
|
async handleTipsSlash(data, adapterId) {
|
|
1555
1577
|
if (!this.fleetConfig)
|
|
1556
1578
|
return;
|
|
@@ -2427,6 +2449,9 @@ export class FleetManager {
|
|
|
2427
2449
|
else if (data.command === "doctor") {
|
|
2428
2450
|
await data.respond(await this.runBackendDoctor());
|
|
2429
2451
|
}
|
|
2452
|
+
else if (data.command === "visibility") {
|
|
2453
|
+
await this.handleVisibilitySlash(data);
|
|
2454
|
+
}
|
|
2430
2455
|
else if (data.command === "usage") {
|
|
2431
2456
|
// Same permission level as /ctx (none). The reply is still ephemeral โ
|
|
2432
2457
|
// the adapter defers non-chat commands that way โ so it never spams the
|
|
@@ -3804,7 +3829,7 @@ export class FleetManager {
|
|
|
3804
3829
|
// Also kill orphaned windows: any window with a topic ID suffix (name-tNNNNN)
|
|
3805
3830
|
// that isn't in the current config โ these are leftovers from deleted instances
|
|
3806
3831
|
const isKnownInstance = agendNames.has(w.name);
|
|
3807
|
-
const isOrphanedInstance = !isKnownInstance && (
|
|
3832
|
+
const isOrphanedInstance = !isKnownInstance && isOrphanInstanceWindowName(w.name);
|
|
3808
3833
|
if (isKnownInstance || isOrphanedInstance) {
|
|
3809
3834
|
if (isOrphanedInstance)
|
|
3810
3835
|
this.logger.info({ window: w.name }, "Cleaning up orphaned tmux window");
|
|
@@ -4510,7 +4535,7 @@ export class FleetManager {
|
|
|
4510
4535
|
}
|
|
4511
4536
|
const problems = [];
|
|
4512
4537
|
const hostMemory = this.memoryPressure.snapshot();
|
|
4513
|
-
if (hostMemory.level === "critical" || hostMemory.level === "elevated") {
|
|
4538
|
+
if (!this.memoryPressure.advisoryOnly() && (hostMemory.level === "critical" || hostMemory.level === "elevated")) {
|
|
4514
4539
|
problems.push(`host memory pressure is ${hostMemory.level}`);
|
|
4515
4540
|
}
|
|
4516
4541
|
if (this.adapterState.size > 0 && connected === 0)
|
|
@@ -5652,10 +5677,9 @@ export class FleetManager {
|
|
|
5652
5677
|
this.bindInstanceAdapter(generalInstance, msg.adapterId, true);
|
|
5653
5678
|
const inboundAdapter = this.worlds.get(msg.adapterId ?? "")?.adapter ?? this.adapter;
|
|
5654
5679
|
// React immediately โ before any other API calls โ through the status
|
|
5655
|
-
// path,
|
|
5656
|
-
// acknowledgement.
|
|
5680
|
+
// path, which tracks Telegram slot ownership and Discord status adds.
|
|
5657
5681
|
if (msg.chatId && msg.messageId) {
|
|
5658
|
-
this.reactMessageStatus(generalInstance, msg.chatId, msg.messageId, "received", msg.threadId || undefined);
|
|
5682
|
+
this.reactMessageStatus(generalInstance, msg.chatId, msg.messageId, "received", msg.threadId || undefined, msg.timestamp.getTime());
|
|
5659
5683
|
}
|
|
5660
5684
|
this.warnIfRateLimited(generalInstance, msg);
|
|
5661
5685
|
const { text, extraMeta } = await processAttachments(msg, inboundAdapter, this.logger, generalInstance);
|
|
@@ -5671,7 +5695,7 @@ export class FleetManager {
|
|
|
5671
5695
|
user: msg.username,
|
|
5672
5696
|
user_id: msg.userId,
|
|
5673
5697
|
ts: msg.timestamp.toISOString(),
|
|
5674
|
-
thread_id: "",
|
|
5698
|
+
thread_id: msg.threadId ?? "",
|
|
5675
5699
|
// Fleet instances have an authoritative adapter binding. Multiple
|
|
5676
5700
|
// bots in one guild can observe the same inbound message, so the
|
|
5677
5701
|
// adapter whose event wins dedup is not necessarily the bot that
|
|
@@ -5744,10 +5768,10 @@ export class FleetManager {
|
|
|
5744
5768
|
this.bindInstanceAdapter(instanceName, msg.adapterId, true);
|
|
5745
5769
|
const inboundAdapter = this.worlds.get(msg.adapterId ?? "")?.adapter ?? this.adapter;
|
|
5746
5770
|
// React immediately โ before any other Discord API calls โ through the
|
|
5747
|
-
// status path,
|
|
5748
|
-
//
|
|
5771
|
+
// status path, which tracks Telegram slot ownership and Discord status adds.
|
|
5772
|
+
// Same bound-adapter routing as the status path itself.
|
|
5749
5773
|
if (msg.chatId && msg.messageId) {
|
|
5750
|
-
this.reactMessageStatus(instanceName, msg.chatId, msg.messageId, "received", msg.threadId || undefined);
|
|
5774
|
+
this.reactMessageStatus(instanceName, msg.chatId, msg.messageId, "received", msg.threadId || undefined, msg.timestamp.getTime());
|
|
5751
5775
|
}
|
|
5752
5776
|
// These may hit Discord API (topic icon, archive) โ do after react
|
|
5753
5777
|
if (this.topicArchiver.isArchived(threadId)) {
|
|
@@ -5976,10 +6000,7 @@ export class FleetManager {
|
|
|
5976
6000
|
respond(null, stickerProblem);
|
|
5977
6001
|
return;
|
|
5978
6002
|
}
|
|
5979
|
-
const
|
|
5980
|
-
const ticket = this.replyDeduper.begin(instanceName,
|
|
5981
|
-
// A reply with stickers is not the same reply as its text alone.
|
|
5982
|
-
stickers.length ? `${String(args.text ?? "")}\u0000stickers:${stickers.join(",")}` : String(args.text ?? ""), Array.isArray(args.files) ? args.files : []);
|
|
6003
|
+
const ticket = this.replyDeduper.begin(instanceName, replyDedupText(args), Array.isArray(args.files) ? args.files : []);
|
|
5983
6004
|
if (ticket.duplicate) {
|
|
5984
6005
|
this.logger.info({ instanceName }, "Concurrent duplicate reply joined โ awaiting the original send's outcome");
|
|
5985
6006
|
ticket.subscribe(respond);
|
|
@@ -6788,16 +6809,23 @@ export class FleetManager {
|
|
|
6788
6809
|
}
|
|
6789
6810
|
return adapter.createTopic(topicName);
|
|
6790
6811
|
}
|
|
6791
|
-
async deleteForumTopic(topicId) {
|
|
6812
|
+
async deleteForumTopic(topicId, adapterId) {
|
|
6792
6813
|
try {
|
|
6793
|
-
|
|
6814
|
+
const adapter = (adapterId ? this.worlds.get(adapterId)?.adapter : undefined) ?? this.adapter;
|
|
6815
|
+
if (!adapter?.deleteTopic)
|
|
6794
6816
|
return;
|
|
6795
|
-
await
|
|
6817
|
+
await adapter.deleteTopic(topicId);
|
|
6796
6818
|
}
|
|
6797
6819
|
catch (err) {
|
|
6798
6820
|
this.logger.warn({ err, topicId }, "Failed to delete forum topic during rollback");
|
|
6799
6821
|
}
|
|
6800
6822
|
}
|
|
6823
|
+
getForumTopicDeleter(adapterId) {
|
|
6824
|
+
const adapter = (adapterId ? this.worlds.get(adapterId)?.adapter : undefined) ?? this.adapter;
|
|
6825
|
+
if (!adapter?.deleteTopic)
|
|
6826
|
+
return null;
|
|
6827
|
+
return (topicId) => adapter.deleteTopic(topicId);
|
|
6828
|
+
}
|
|
6801
6829
|
topicCleanupTimer = null;
|
|
6802
6830
|
sessionPruneTimer = null;
|
|
6803
6831
|
classicReloadTimer = null;
|
|
@@ -7883,6 +7911,25 @@ export class FleetManager {
|
|
|
7883
7911
|
const resolved = this.resolveStatusEmojisFor(instanceName, adapterId, adapter);
|
|
7884
7912
|
return reactionForm(resolved.platform, resolved.received);
|
|
7885
7913
|
}
|
|
7914
|
+
reactClassicReceived(instanceName, adapter, msg) {
|
|
7915
|
+
const emoji = this.receivedReactionFor(instanceName, adapter, msg.adapterId);
|
|
7916
|
+
// Classic's system receipt is status-owned too. An ordinary react call
|
|
7917
|
+
// would label it as an agent/other reaction and suppress later statuses.
|
|
7918
|
+
if (adapter instanceof TelegramAdapter) {
|
|
7919
|
+
return adapter.reactDeliveryStatus(msg.chatId, msg.messageId, emoji, msg.timestamp.getTime()).then(() => { });
|
|
7920
|
+
}
|
|
7921
|
+
return adapter.react(msg.threadId ?? msg.chatId, msg.messageId, emoji);
|
|
7922
|
+
}
|
|
7923
|
+
reactClassicForwardedAttachment(instanceName, adapter, msg, kind) {
|
|
7924
|
+
const emoji = this.savedAttachmentReactionFor(instanceName, adapter, msg.adapterId, kind);
|
|
7925
|
+
// A forwarded attachment's saved stamp is ours, so later delivery statuses
|
|
7926
|
+
// may replace it. The receipt queued first establishes ownership; this
|
|
7927
|
+
// stamp cannot bootstrap an unknown slot or replace an agent's reaction.
|
|
7928
|
+
if (adapter instanceof TelegramAdapter) {
|
|
7929
|
+
return adapter.reactDeliveryStatus(msg.chatId, msg.messageId, emoji).then(() => { });
|
|
7930
|
+
}
|
|
7931
|
+
return adapter.react(msg.threadId ?? msg.chatId, msg.messageId, emoji);
|
|
7932
|
+
}
|
|
7886
7933
|
/**
|
|
7887
7934
|
* The stamp for an inbound photo / file a classic bot saved (#1080): the
|
|
7888
7935
|
* instance's `status_emojis.photo|attachment`, then its connection's, then
|
|
@@ -7893,7 +7940,7 @@ export class FleetManager {
|
|
|
7893
7940
|
const resolved = this.resolveStatusEmojisFor(instanceName, adapterId, adapter);
|
|
7894
7941
|
return reactionForm(resolved.platform, kind === "photo" ? resolved.photo : resolved.attachment);
|
|
7895
7942
|
}
|
|
7896
|
-
reactMessageStatus(instanceName, chatId, messageId, status, threadId) {
|
|
7943
|
+
reactMessageStatus(instanceName, chatId, messageId, status, threadId, receivedAt) {
|
|
7897
7944
|
// React via the adapter BOUND to this instance โ NOT the first discord world.
|
|
7898
7945
|
// Otherwise, in a same-channel/same-guild multi-bot setup, the inbound ๐
|
|
7899
7946
|
// (bound bot) and the delivery/confirm reactions (some other bot) come from
|
|
@@ -7914,7 +7961,7 @@ export class FleetManager {
|
|
|
7914
7961
|
const adapterId = typeof adapter.id === "string"
|
|
7915
7962
|
? adapter.id : "?";
|
|
7916
7963
|
const key = `${adapterId}:${chatId}:${threadId ?? ""}:${messageId}`;
|
|
7917
|
-
this.queueDeliveryStatusReaction(adapter, key, chatId, messageId, statusEmoji, threadId, status);
|
|
7964
|
+
this.queueDeliveryStatusReaction(adapter, key, chatId, messageId, statusEmoji, threadId, status, status === "received" ? receivedAt : undefined);
|
|
7918
7965
|
}
|
|
7919
7966
|
/**
|
|
7920
7967
|
* Apply a terminal delivery verdict. On Telegram the built-in delivered is
|
|
@@ -7923,19 +7970,30 @@ export class FleetManager {
|
|
|
7923
7970
|
finishDeliveryStatus(instanceName, chatId, messageId, status, threadId) {
|
|
7924
7971
|
this.reactMessageStatus(instanceName, chatId, messageId, status, threadId);
|
|
7925
7972
|
}
|
|
7926
|
-
queueDeliveryStatusReaction(adapter, key, chatId, messageId, emoji, threadId, status) {
|
|
7973
|
+
queueDeliveryStatusReaction(adapter, key, chatId, messageId, emoji, threadId, status, receivedAt) {
|
|
7927
7974
|
const prev = this.deliveryStatusChains.get(key) ?? Promise.resolve();
|
|
7928
|
-
const run = prev.then(() => this.applyDeliveryStatusReaction(adapter, key, chatId, messageId, emoji, threadId, status));
|
|
7975
|
+
const run = prev.then(() => this.applyDeliveryStatusReaction(adapter, key, chatId, messageId, emoji, threadId, status, receivedAt));
|
|
7929
7976
|
this.deliveryStatusChains.set(key, run);
|
|
7930
7977
|
void run.then(() => { if (this.deliveryStatusChains.get(key) === run)
|
|
7931
7978
|
this.deliveryStatusChains.delete(key); }, () => { if (this.deliveryStatusChains.get(key) === run)
|
|
7932
7979
|
this.deliveryStatusChains.delete(key); });
|
|
7933
7980
|
}
|
|
7934
|
-
async applyDeliveryStatusReaction(adapter, key, chatId, messageId, emoji, threadId, status) {
|
|
7981
|
+
async applyDeliveryStatusReaction(adapter, key, chatId, messageId, emoji, threadId, status, receivedAt) {
|
|
7935
7982
|
try {
|
|
7936
7983
|
const target = adapter instanceof TelegramAdapter ? chatId : (threadId ?? chatId);
|
|
7937
7984
|
const last = this.lastStatusEmoji.get(key);
|
|
7938
7985
|
const prev = last?.emoji;
|
|
7986
|
+
if (adapter instanceof TelegramAdapter) {
|
|
7987
|
+
// The adapter owns the one-reaction slot and serializes this check
|
|
7988
|
+
// with agent react calls. The fleet's last emoji is not ownership.
|
|
7989
|
+
if (await adapter.reactDeliveryStatus(chatId, messageId, emoji, receivedAt)) {
|
|
7990
|
+
if (emoji == null)
|
|
7991
|
+
this.lastStatusEmoji.delete(key);
|
|
7992
|
+
else
|
|
7993
|
+
this.lastStatusEmoji.set(key, { emoji, status });
|
|
7994
|
+
}
|
|
7995
|
+
return;
|
|
7996
|
+
}
|
|
7939
7997
|
if (emoji == null) {
|
|
7940
7998
|
if (prev && adapter.unreact) {
|
|
7941
7999
|
await adapter.unreact(target, messageId, prev, threadId);
|
|
@@ -7943,9 +8001,7 @@ export class FleetManager {
|
|
|
7943
8001
|
}
|
|
7944
8002
|
return;
|
|
7945
8003
|
}
|
|
7946
|
-
//
|
|
7947
|
-
// reaction. Avoid resending it: an agent may have replaced the one
|
|
7948
|
-
// reaction slot with its own ๐ between delivered and confirmed.
|
|
8004
|
+
// Discord adds reactions rather than replacing a single slot.
|
|
7949
8005
|
if (prev === emoji)
|
|
7950
8006
|
return;
|
|
7951
8007
|
// Thread-aware adapters (Discord) react where the thread is; Telegram
|
|
@@ -9847,9 +9903,8 @@ export class FleetManager {
|
|
|
9847
9903
|
const installed = this.probeInstalledBackends();
|
|
9848
9904
|
// One entry point for "get this CLI working" (#1131): a backend that is
|
|
9849
9905
|
// not installed is offered too, and the click installs it first, then
|
|
9850
|
-
// signs in (startLoginSession routes it).
|
|
9851
|
-
|
|
9852
|
-
const installable = new Set(Object.keys(BACKEND_INSTALLATION_INFO).filter(backend => backend !== "gemini-cli"));
|
|
9906
|
+
// signs in (startLoginSession routes it).
|
|
9907
|
+
const installable = new Set(Object.keys(BACKEND_INSTALLATION_INFO));
|
|
9853
9908
|
const candidates = new Set([...installed, ...configured, ...installable]);
|
|
9854
9909
|
const unsupported = [];
|
|
9855
9910
|
const choices = [...candidates].sort().flatMap(backend => {
|
|
@@ -9873,11 +9928,13 @@ export class FleetManager {
|
|
|
9873
9928
|
return [];
|
|
9874
9929
|
});
|
|
9875
9930
|
if (unsupported.length) {
|
|
9876
|
-
const guidance = unsupported.map(({ backend, flow, status }) => `${backend} ยท ${status.join(" ยท ")} โ ${
|
|
9877
|
-
? (
|
|
9878
|
-
:
|
|
9879
|
-
? t("login.
|
|
9880
|
-
:
|
|
9931
|
+
const guidance = unsupported.map(({ backend, flow, status }) => `${backend} ยท ${status.join(" ยท ")} โ ${isRemovedBackend(backend)
|
|
9932
|
+
? removedBackendMessage(backend)
|
|
9933
|
+
: !installed.has(backend)
|
|
9934
|
+
? (BACKEND_INSTALLATION_INFO[backend] ? t("login.install_by_name", backend) : t("login.install_on_host", backend))
|
|
9935
|
+
: flow?.remoteLogin === "unsupported"
|
|
9936
|
+
? t("login.remote_unsupported_agent_cli", backend, flow.command)
|
|
9937
|
+
: backend === "opencode" ? t("login.unsupported", backend) : t("login.no_remote_flow", backend)}`).join("\n");
|
|
9881
9938
|
await chat.adapter.sendText(chat.chatId, guidance, { threadId: chat.threadId })
|
|
9882
9939
|
.catch(err => this.logger.warn({ err }, "Failed to post unsupported login guidance"));
|
|
9883
9940
|
}
|
|
@@ -10024,6 +10081,9 @@ export class FleetManager {
|
|
|
10024
10081
|
if (opts.skipAuthCheck)
|
|
10025
10082
|
return this.launchSignIn(backendArg, chat, opts);
|
|
10026
10083
|
const wanted = LOGIN_BACKEND_ALIASES[backendArg.toLowerCase()] ?? backendArg.toLowerCase();
|
|
10084
|
+
// A removed backend (#1280) is neither installed nor signed into: say what replaces it.
|
|
10085
|
+
if (isRemovedBackend(wanted))
|
|
10086
|
+
return removedBackendMessage(wanted);
|
|
10027
10087
|
if (BACKEND_INSTALLATION_INFO[wanted] && !this.isCliInstalled(wanted)) {
|
|
10028
10088
|
const flow = LOGIN_FLOWS[wanted];
|
|
10029
10089
|
this.recordLoginFlow(wanted, flow && flow.remoteLogin !== "unsupported" ? "install_then_login" : "install_only", chat.userId);
|
|
@@ -10538,7 +10598,6 @@ export class FleetManager {
|
|
|
10538
10598
|
static INSTRUCTIONS_FILENAME = {
|
|
10539
10599
|
"claude-code": "CLAUDE.md",
|
|
10540
10600
|
"codex": "AGENTS.md",
|
|
10541
|
-
"gemini-cli": "GEMINI.md",
|
|
10542
10601
|
"opencode": "AGENTS.md",
|
|
10543
10602
|
"kiro-cli": ".kiro/steering/project.md",
|
|
10544
10603
|
// Grok reads AGENTS.md project docs; agy reads .agents/agents.md โ the
|
|
@@ -10609,7 +10668,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
10609
10668
|
/**
|
|
10610
10669
|
* Where each backend natively loads on-demand skills from, relative to the
|
|
10611
10670
|
* workspace. Backends without a native skill mechanism (opencode, grok,
|
|
10612
|
-
* antigravity
|
|
10671
|
+
* antigravity) are deliberately absent: dropping files a CLI
|
|
10613
10672
|
* never reads is clutter, not capability. Unknown directories are ignored
|
|
10614
10673
|
* by older CLI versions, so publishing is fail-open across upgrades.
|
|
10615
10674
|
*/
|
|
@@ -10946,7 +11005,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
10946
11005
|
const classicAdapter = this.worlds.get(msg.adapterId ?? "")?.adapter ?? this.adapter;
|
|
10947
11006
|
const collabReactChatId = msg.threadId ?? msg.chatId;
|
|
10948
11007
|
if (classicAdapter && collabReactChatId && msg.messageId) {
|
|
10949
|
-
|
|
11008
|
+
this.reactClassicReceived(instanceName, classicAdapter, msg)
|
|
10950
11009
|
.catch(e => this.logger.debug({ err: e.message }, "Auto-react failed"));
|
|
10951
11010
|
}
|
|
10952
11011
|
// Block /raw bypass โ check the mention-stripped text so the self-marker
|
|
@@ -10955,8 +11014,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
10955
11014
|
return;
|
|
10956
11015
|
// Attachments already saved at the top of the collab block.
|
|
10957
11016
|
if (saved && classicAdapter && collabReactChatId && msg.messageId) {
|
|
10958
|
-
|
|
10959
|
-
classicAdapter.react(collabReactChatId, msg.messageId, emoji)
|
|
11017
|
+
this.reactClassicForwardedAttachment(instanceName, classicAdapter, msg, saved.kind)
|
|
10960
11018
|
.catch(e => this.logger.debug({ err: e.message }, "Auto-react failed"));
|
|
10961
11019
|
}
|
|
10962
11020
|
// Strip saved attachment to avoid double download
|
|
@@ -11045,12 +11103,10 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
11045
11103
|
}
|
|
11046
11104
|
}
|
|
11047
11105
|
if (msg.chatId && msg.messageId) {
|
|
11048
|
-
|
|
11049
|
-
classicMsgAdapter.react(reactChatId, msg.messageId, this.receivedReactionFor(instanceName, classicMsgAdapter, msg.adapterId))
|
|
11106
|
+
this.reactClassicReceived(instanceName, classicMsgAdapter, msg)
|
|
11050
11107
|
.catch(e => this.logger.debug({ err: e.message }, "Auto-react failed"));
|
|
11051
11108
|
if (saved) {
|
|
11052
|
-
|
|
11053
|
-
classicMsgAdapter.react(reactChatId, msg.messageId, savedEmoji)
|
|
11109
|
+
this.reactClassicForwardedAttachment(instanceName, classicMsgAdapter, msg, saved.kind)
|
|
11054
11110
|
.catch(e => this.logger.debug({ err: e.message }, "Auto-react failed"));
|
|
11055
11111
|
}
|
|
11056
11112
|
}
|
|
@@ -14289,17 +14345,18 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
14289
14345
|
const currentVersion = JSON.parse(readFileSync(pkgPath, "utf-8")).version ?? "0.0.0";
|
|
14290
14346
|
const latest = await npmVersion("@songsid/agend");
|
|
14291
14347
|
let target = latest;
|
|
14292
|
-
// The same channel rule as `agend update` (
|
|
14293
|
-
|
|
14294
|
-
|
|
14295
|
-
//
|
|
14296
|
-
//
|
|
14297
|
-
|
|
14348
|
+
// The same channel rule as `agend update` (#1259): an alpha follows @alpha, any other prerelease @beta.
|
|
14349
|
+
const channel = installedChannel(currentVersion);
|
|
14350
|
+
if (channel !== "latest") {
|
|
14351
|
+
// Prerelease users track their own channel (never fall back to @latest,
|
|
14352
|
+
// which is older, and an alpha is never offered @beta), but should also
|
|
14353
|
+
// hear when a newer STABLE ships โ pick whichever is the newest.
|
|
14354
|
+
let pre = "";
|
|
14298
14355
|
try {
|
|
14299
|
-
|
|
14356
|
+
pre = await npmVersion(`@songsid/agend@${channel}`);
|
|
14300
14357
|
}
|
|
14301
|
-
catch { /* no
|
|
14302
|
-
target =
|
|
14358
|
+
catch { /* no such tag */ }
|
|
14359
|
+
target = pre || latest;
|
|
14303
14360
|
if (latest && this.semverGt(latest, target))
|
|
14304
14361
|
target = latest;
|
|
14305
14362
|
}
|