@songsid/agend 2.2.0-beta.2 → 2.2.0-beta.3
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/auth-api.js +2 -0
- package/dist/auth-api.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +1 -1
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +12 -2
- package/dist/channel/adapters/telegram.js +39 -8
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/telegram-markdown.d.ts +6 -0
- package/dist/channel/telegram-markdown.js +104 -0
- package/dist/channel/telegram-markdown.js.map +1 -0
- package/dist/channel/tool-router.js +5 -4
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/channel/types.d.ts +5 -3
- package/dist/config-validator.js +6 -5
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.d.ts +12 -0
- package/dist/daemon.js +93 -16
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +11 -2
- package/dist/delivery-outbox.js +13 -2
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-reconciliation.d.ts +18 -1
- package/dist/delivery-reconciliation.js +67 -2
- package/dist/delivery-reconciliation.js.map +1 -1
- package/dist/fleet-manager.d.ts +22 -1
- package/dist/fleet-manager.js +96 -15
- package/dist/fleet-manager.js.map +1 -1
- package/dist/locale.js +4 -0
- package/dist/locale.js.map +1 -1
- package/dist/outbound-handlers.d.ts +2 -0
- package/dist/outbound-handlers.js +21 -11
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +2 -2
- package/dist/outbound-schemas.js +1 -1
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +4 -2
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/settings-change.js +2 -2
- package/dist/tips.js +1 -1
- package/dist/tips.js.map +1 -1
- package/dist/transcript-delta-reader.d.ts +3 -3
- package/dist/transcript-delta-reader.js +8 -8
- package/dist/transcript-delta-reader.js.map +1 -1
- package/dist/ui/chat-store.js +1 -1
- package/dist/ui/chat-strings.js +12 -12
- package/dist/ui/chat-thread.js +22 -3
- package/dist/ui/panel-chat.js +3 -0
- package/dist/ui/panel-settings.js +15 -8
- package/dist/ui/settings-strings.js +6 -2
- package/dist/ui/shared/app-i18n.js +35 -13
- package/dist/ui/shared/app-prefs.js +249 -0
- package/dist/ui/shared/app-shell.js +17 -4
- package/dist/ui/shared/app-store.js +3 -1
- package/dist/ui/shared/app.css +30 -0
- package/dist/ui/shared/app.js +6 -1
- package/dist/ui/shared/header-tools.js +55 -5
- package/dist/ui/shared/instance-nav.js +99 -31
- package/dist/ui/shared/panel-view.js +6 -0
- package/dist/ui/shared/theme.js +39 -7
- package/dist/ui/shared/ui-icons.js +1 -0
- package/dist/ui/shared/usage-dialog.js +13 -4
- package/dist/ui/shared/view-strings.js +20 -2
- package/dist/view-api.js +5 -0
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +10 -1
- package/dist/web-api.js +103 -1
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +2 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-prefs.d.ts +63 -0
- package/dist/web-prefs.js +263 -0
- package/dist/web-prefs.js.map +1 -0
- package/dist/web-session.js +1 -1
- package/dist/web-session.js.map +1 -1
- package/package.json +1 -1
package/dist/fleet-manager.js
CHANGED
|
@@ -75,7 +75,7 @@ import { InstanceLifecycle, SupersededStartError, BACKEND_INSTALLATION_INFO, che
|
|
|
75
75
|
import { TopicArchiver } from "./topic-archiver.js";
|
|
76
76
|
import { StatuslineWatcher } from "./statusline-watcher.js";
|
|
77
77
|
import { outboundHandlers } from "./outbound-handlers.js";
|
|
78
|
-
import { DeliveryOutbox } from "./delivery-outbox.js";
|
|
78
|
+
import { DeliveryOutbox, isSilentScheduleDelivery } from "./delivery-outbox.js";
|
|
79
79
|
// The target Daemon can legitimately wait 30 minutes for a busy pane before
|
|
80
80
|
// deciding to defer before submission. This is an alert threshold only: the
|
|
81
81
|
// active manager/target generation pair owns the lane until the daemon reports
|
|
@@ -129,6 +129,7 @@ export function applyTaskListCap(tasks, filterAssignee, filterStatus) {
|
|
|
129
129
|
import { handleWebRequest, broadcastSseEvent, SSE_HEARTBEAT_MS } from "./web-api.js";
|
|
130
130
|
import { WebChatHistory, WEB_CHAT_TEXT_MAX, isWebMessageId, newWebMessageId } from "./web-chat-history.js";
|
|
131
131
|
import { WebChatDiskStore } from "./web-chat-store.js";
|
|
132
|
+
import { WebPrefsStore } from "./web-prefs.js";
|
|
132
133
|
import { ReplyButtonStore, parseReplyButtons, replyButtonClickText, replyButtonsFallbackText, REPLY_BUTTON_PREFIX } from "./reply-buttons.js";
|
|
133
134
|
import { ReplyButtonsController } from "./reply-buttons-controller.js";
|
|
134
135
|
import { publicAttachment, sweepOrphanedUploads, WebFileLedger } from "./web-upload.js";
|
|
@@ -762,6 +763,15 @@ export class FleetManager {
|
|
|
762
763
|
webChatStore = null;
|
|
763
764
|
/** Uploaded files and the files the dashboard may fetch back (uploads, reply attachments). */
|
|
764
765
|
webFiles = new WebFileLedger();
|
|
766
|
+
webPrefsStore;
|
|
767
|
+
/** #1665: the web preferences kept on the host (<home>/web-prefs.json), read on first use. */
|
|
768
|
+
get webPrefs() {
|
|
769
|
+
// #1664: a favorite must be an instance of this fleet now — fleet.yaml's (own keys) or a ClassicBot room's.
|
|
770
|
+
return (this.webPrefsStore ??= new WebPrefsStore(this.dataDir, {
|
|
771
|
+
exists: (name) => Object.hasOwn(this.fleetConfig?.instances ?? {}, name)
|
|
772
|
+
|| (this.classicChannels?.getAll() ?? []).some((ch) => ch.instanceName === name),
|
|
773
|
+
}));
|
|
774
|
+
}
|
|
765
775
|
/**
|
|
766
776
|
* Read from disk on every access rather than cached at startup: `agend
|
|
767
777
|
* web-token rotate` runs in a separate process, and a cached copy would keep
|
|
@@ -1219,8 +1229,13 @@ export class FleetManager {
|
|
|
1219
1229
|
if (event.deliveryId && (event.state === "failed" || event.state === "uncertain")) {
|
|
1220
1230
|
const row = outbox.get(event.deliveryId);
|
|
1221
1231
|
if (row && row.kind !== "delivery_outcome_notice" && row.kind !== "post_restart_outcome_notice") {
|
|
1222
|
-
|
|
1223
|
-
|
|
1232
|
+
// #1647: the cause goes in the log (the notice points there), and a silent schedule's paste says what it is.
|
|
1233
|
+
const silent = isSilentScheduleDelivery(row);
|
|
1234
|
+
const notice = silent
|
|
1235
|
+
? t(`delivery.fleet_${row.state}_silent_schedule`, row.targetInstance, row.operationId, row.deliveryId, row.lastError ?? "unknown")
|
|
1236
|
+
: t(`delivery.fleet_${row.state}`, row.targetInstance, row.operationId, row.deliveryId);
|
|
1237
|
+
this.logger.error({ deliveryId: row.deliveryId, operationId: row.operationId, target: row.targetInstance, state: row.state,
|
|
1238
|
+
cause: row.lastError, ...(silent ? { silentSchedule: true } : {}) }, notice);
|
|
1224
1239
|
this.notifyFleetError(notice);
|
|
1225
1240
|
}
|
|
1226
1241
|
}
|
|
@@ -3020,13 +3035,38 @@ export class FleetManager {
|
|
|
3020
3035
|
}
|
|
3021
3036
|
return posted;
|
|
3022
3037
|
}
|
|
3023
|
-
|
|
3038
|
+
/**
|
|
3039
|
+
* #1666: a delivered web chat message starts the same cancel/progress lifecycle in the instance's topic as a platform
|
|
3040
|
+
* inbound (afterUserInboundDelivered): the same bubble, ticker and retire rules. Not its ✅ tracking — the web message
|
|
3041
|
+
* id is no platform message, and the echo is the fleet's own post. The web send calls this on the echo's lane.
|
|
3042
|
+
*/
|
|
3043
|
+
startWebChatProgress(instanceName) {
|
|
3044
|
+
if (this.shuttingDown)
|
|
3045
|
+
return Promise.resolve();
|
|
3046
|
+
return this.sendCancelButton(instanceName);
|
|
3047
|
+
}
|
|
3048
|
+
/**
|
|
3049
|
+
* `copy: false` (#1666/#1668): an entry that is not a copy of a message — the web send's Cancel/progress bubble. It
|
|
3050
|
+
* keeps its place in the order, but the lane's copy rules do not apply to it: its 5 s budget starts when its turn
|
|
3051
|
+
* starts (waiting behind a slow echo is not its delay), its own timeout only releases the lane (the copies queued
|
|
3052
|
+
* behind it are not "behind an ambiguous copy"), and another entry's timeout never drops it. A copy reserved while
|
|
3053
|
+
* any non-copy is still open starts its own budget when the last of them finishes: a slow bubble is no copy's delay,
|
|
3054
|
+
* however far back the copy is. And a timeout only ever drops copies reserved AFTER the entry that timed out — with
|
|
3055
|
+
* deferred budgets, deadlines no longer grow with the order, so that rule is stated, not implied (#1668 r3).
|
|
3056
|
+
*/
|
|
3057
|
+
reserveWebChannelEcho(instanceName, sendEcho, opts = {}) {
|
|
3058
|
+
const copy = opts.copy !== false;
|
|
3024
3059
|
const epoch = this.getDeliveryEpoch(instanceName);
|
|
3025
|
-
const
|
|
3060
|
+
const queued = this.webChannelEchoTails.get(instanceName);
|
|
3061
|
+
// The last open non-copy ahead of this copy: its budget starts when that one finishes.
|
|
3062
|
+
const nonCopyAhead = copy && (queued?.nonCopiesOpen ?? 0) > 0 ? queued?.lastNonCopyDone : undefined;
|
|
3063
|
+
let deadlineAt = copy && !nonCopyAhead ? performance.now() + 5_000 : Number.POSITIVE_INFINITY;
|
|
3026
3064
|
let decide;
|
|
3027
3065
|
const admission = new Promise(resolve => { decide = resolve; });
|
|
3028
|
-
const queue =
|
|
3066
|
+
const queue = queued
|
|
3029
3067
|
?? { tail: Promise.resolve(), pending: new Set() };
|
|
3068
|
+
const seq = queue.nextSeq ?? 0;
|
|
3069
|
+
queue.nextSeq = seq + 1;
|
|
3030
3070
|
const previous = queue.tail;
|
|
3031
3071
|
let resolveDone;
|
|
3032
3072
|
const tail = new Promise(resolve => { resolveDone = resolve; });
|
|
@@ -3037,6 +3077,8 @@ export class FleetManager {
|
|
|
3037
3077
|
if (done)
|
|
3038
3078
|
return;
|
|
3039
3079
|
done = true;
|
|
3080
|
+
if (!copy)
|
|
3081
|
+
queue.nonCopiesOpen = Math.max(0, (queue.nonCopiesOpen ?? 0) - 1);
|
|
3040
3082
|
clearTimeout(timer);
|
|
3041
3083
|
queue.pending.delete(entry);
|
|
3042
3084
|
if (guard && this.webChannelEchoGuards.get(instanceName) === guard) {
|
|
@@ -3044,7 +3086,7 @@ export class FleetManager {
|
|
|
3044
3086
|
}
|
|
3045
3087
|
resolveDone();
|
|
3046
3088
|
};
|
|
3047
|
-
const entry = { started: false, drop: () => {
|
|
3089
|
+
const entry = { seq, started: false, copy, drop: () => {
|
|
3048
3090
|
this.logger.warn({ instanceName }, "Queued web channel echo dropped after ordering timeout");
|
|
3049
3091
|
finish();
|
|
3050
3092
|
} };
|
|
@@ -3054,14 +3096,16 @@ export class FleetManager {
|
|
|
3054
3096
|
expired = true;
|
|
3055
3097
|
this.logger.warn({ instanceName, inFlight: entry.started }, "Web channel echo ordering timed out");
|
|
3056
3098
|
finish();
|
|
3057
|
-
// Do not post copies that were queued behind an ambiguous platform send.
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3099
|
+
// Do not post copies that were queued behind an ambiguous platform send. A non-copy that timed out was no copy
|
|
3100
|
+
// at all (nothing behind it is ambiguous), and a non-copy is never dropped as one.
|
|
3101
|
+
if (copy)
|
|
3102
|
+
for (const pending of queue.pending)
|
|
3103
|
+
if (pending.seq > seq && !pending.started && pending.copy)
|
|
3104
|
+
pending.drop();
|
|
3061
3105
|
};
|
|
3062
3106
|
const checkDeadline = () => {
|
|
3063
|
-
if (done)
|
|
3064
|
-
return;
|
|
3107
|
+
if (done || deadlineAt === Number.POSITIVE_INFINITY)
|
|
3108
|
+
return; // a non-copy's budget has not started yet
|
|
3065
3109
|
const remaining = deadlineAt - performance.now();
|
|
3066
3110
|
if (remaining <= 0)
|
|
3067
3111
|
expire();
|
|
@@ -3070,8 +3114,19 @@ export class FleetManager {
|
|
|
3070
3114
|
};
|
|
3071
3115
|
queue.pending.add(entry);
|
|
3072
3116
|
queue.tail = tail;
|
|
3117
|
+
if (!copy) {
|
|
3118
|
+
queue.nonCopiesOpen = (queue.nonCopiesOpen ?? 0) + 1;
|
|
3119
|
+
queue.lastNonCopyDone = tail;
|
|
3120
|
+
}
|
|
3073
3121
|
this.webChannelEchoTails.set(instanceName, queue);
|
|
3074
3122
|
checkDeadline();
|
|
3123
|
+
// Registered before the admission below (which waits on `previous`, itself after the non-copy ahead), so the budget
|
|
3124
|
+
// is set by the time that runs.
|
|
3125
|
+
if (nonCopyAhead)
|
|
3126
|
+
void nonCopyAhead.then(() => { if (!done) {
|
|
3127
|
+
deadlineAt = performance.now() + 5_000;
|
|
3128
|
+
checkDeadline();
|
|
3129
|
+
} });
|
|
3075
3130
|
void Promise.all([previous, admission]).then(([, accepted]) => {
|
|
3076
3131
|
if (done)
|
|
3077
3132
|
return;
|
|
@@ -3080,11 +3135,15 @@ export class FleetManager {
|
|
|
3080
3135
|
return;
|
|
3081
3136
|
}
|
|
3082
3137
|
// A delayed timer callback must not admit an already expired copy.
|
|
3083
|
-
if (performance.now() >= deadlineAt) {
|
|
3138
|
+
if (copy && performance.now() >= deadlineAt) {
|
|
3084
3139
|
expire();
|
|
3085
3140
|
return;
|
|
3086
3141
|
}
|
|
3087
3142
|
entry.started = true;
|
|
3143
|
+
if (!copy) {
|
|
3144
|
+
deadlineAt = performance.now() + 5_000;
|
|
3145
|
+
checkDeadline();
|
|
3146
|
+
}
|
|
3088
3147
|
guard = { epoch, deadlineAt };
|
|
3089
3148
|
this.webChannelEchoGuards.set(instanceName, guard);
|
|
3090
3149
|
let request;
|
|
@@ -9350,7 +9409,9 @@ export class FleetManager {
|
|
|
9350
9409
|
else
|
|
9351
9410
|
await this.lifecycle.remove(name, authorization);
|
|
9352
9411
|
// A later same-name registration owns its files and caches.
|
|
9353
|
-
|
|
9412
|
+
// Own keys only: an instance named "constructor" is removed as any other (its inherited namesake is not a
|
|
9413
|
+
// re-registration) — #1677 review.
|
|
9414
|
+
if (Object.hasOwn(this.fleetConfig?.instances ?? {}, name) || this.daemons.has(name))
|
|
9354
9415
|
return;
|
|
9355
9416
|
execution?.assert();
|
|
9356
9417
|
// Clean up per-instance tracking maps so they don't grow unbounded
|
|
@@ -9362,6 +9423,13 @@ export class FleetManager {
|
|
|
9362
9423
|
// must not be shown the old one's conversation, and deleted names must not pile up.
|
|
9363
9424
|
this.webChatHistory.forget(name);
|
|
9364
9425
|
this.webFiles.forget(name);
|
|
9426
|
+
// #1664: and it is nobody's favorite any more (every device sees that at its next status frame).
|
|
9427
|
+
try {
|
|
9428
|
+
this.webPrefs.forgetInstance(name);
|
|
9429
|
+
}
|
|
9430
|
+
catch (err) {
|
|
9431
|
+
this.logger.warn({ err: err.message, name }, "Dropping a removed instance from web favorites failed");
|
|
9432
|
+
}
|
|
9365
9433
|
// Clean up statusline watcher + instance directory
|
|
9366
9434
|
this.statuslineWatcher.unwatch(name);
|
|
9367
9435
|
// instanceDirToClean and effectiveBackend are captured before lifecycle.remove
|
|
@@ -15639,6 +15707,17 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
15639
15707
|
const ch = this.classicChannels.unregister(channelId, adapterId);
|
|
15640
15708
|
if (!ch)
|
|
15641
15709
|
return t("classic.no_agent");
|
|
15710
|
+
// #1664: the room is gone (not paused or stopped): nobody's favorite any more — unless the name is still an
|
|
15711
|
+
// instance (fleet.yaml's, or another room's), so a later room of the same name does not inherit an old star.
|
|
15712
|
+
if (!Object.hasOwn(this.fleetConfig?.instances ?? {}, ch.instanceName)
|
|
15713
|
+
&& !this.classicChannels.getAll().some((c) => c.instanceName === ch.instanceName)) {
|
|
15714
|
+
try {
|
|
15715
|
+
this.webPrefs.forgetInstance(ch.instanceName);
|
|
15716
|
+
}
|
|
15717
|
+
catch (err) {
|
|
15718
|
+
this.logger.warn({ err: err.message, instanceName: ch.instanceName }, "Dropping a removed room from web favorites failed");
|
|
15719
|
+
}
|
|
15720
|
+
}
|
|
15642
15721
|
this.instanceWorldBinding.delete(ch.instanceName);
|
|
15643
15722
|
await this.stopInstance(ch.instanceName).catch(err => this.logger.warn({ err, instanceName: ch.instanceName }, "Failed to stop classic instance"));
|
|
15644
15723
|
clearPausedMarker(this.getInstanceDir(ch.instanceName));
|
|
@@ -18357,6 +18436,8 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
18357
18436
|
uptime: Math.floor((Date.now() - this.startedAt) / 1000),
|
|
18358
18437
|
// #1519 P7: how many chat connections are configured — 0 is a web-only fleet, which gets the first-run card.
|
|
18359
18438
|
connections: (this.fleetConfig?.channels ?? (this.fleetConfig?.channel ? [this.fleetConfig.channel] : [])).length,
|
|
18439
|
+
// #1665: the host preferences' revision — a page that has another re-reads /ui/prefs (no values in this frame).
|
|
18440
|
+
prefs_rev: this.webPrefs.rev(),
|
|
18360
18441
|
};
|
|
18361
18442
|
}
|
|
18362
18443
|
}
|