@songsid/agend 2.3.2 → 2.3.3-beta.2
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/agent-cli-instructions.md +2 -2
- package/dist/agent-whats-new.d.ts +21 -0
- package/dist/agent-whats-new.js +52 -23
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/authority/authority-state.js +1 -1
- package/dist/authority/authority-state.js.map +1 -1
- package/dist/authority/web-authority.d.ts +9 -4
- package/dist/authority/web-authority.js +10 -5
- package/dist/authority/web-authority.js.map +1 -1
- package/dist/backend/claude-code.d.ts +13 -1
- package/dist/backend/claude-code.js +20 -1
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex-session-lookup.d.ts +16 -3
- package/dist/backend/codex-session-lookup.js +45 -11
- package/dist/backend/codex-session-lookup.js.map +1 -1
- package/dist/backend/kiro-reconcile-store.js +8 -1
- package/dist/backend/kiro-reconcile-store.js.map +1 -1
- package/dist/backend/kiro.d.ts +0 -5
- package/dist/backend/kiro.js +0 -10
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +7 -4
- package/dist/backend/types.js.map +1 -1
- package/dist/cli.js +18 -3
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +4 -5
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +4 -2
- package/dist/config.js.map +1 -1
- package/dist/cross-instance-envelope.d.ts +10 -5
- package/dist/cross-instance-envelope.js +20 -17
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +90 -63
- package/dist/daemon.js +412 -317
- package/dist/daemon.js.map +1 -1
- package/dist/daily-summary.d.ts +8 -1
- package/dist/daily-summary.js +8 -5
- package/dist/daily-summary.js.map +1 -1
- package/dist/delivery-outbox.d.ts +31 -22
- package/dist/delivery-outbox.js +119 -161
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-outbox.test.js +10 -25
- package/dist/delivery-outbox.test.js.map +1 -1
- package/dist/fleet-level-config.js +1 -1
- package/dist/fleet-level-config.js.map +1 -1
- package/dist/fleet-manager.d.ts +4 -21
- package/dist/fleet-manager.js +38 -159
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-service-owner.d.ts +11 -0
- package/dist/fleet-service-owner.js +3 -0
- package/dist/fleet-service-owner.js.map +1 -0
- package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +1 -1
- package/dist/general-knowledge/skills/fleet-restart/SKILL.md +1 -1
- package/dist/general-knowledge/skills/session-management/SKILL.md +1 -1
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +4 -5
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +4 -5
- package/dist/instance-lifecycle.js +23 -5
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.js +6 -7
- package/dist/instructions.js.map +1 -1
- package/dist/kiro-reconcile.js +3 -3
- package/dist/kiro-reconcile.js.map +1 -1
- package/dist/locale.js +8 -52
- package/dist/locale.js.map +1 -1
- package/dist/log-service.js +1 -1
- package/dist/log-service.js.map +1 -1
- package/dist/needs-you-hub.js +4 -1
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you-live.d.ts +3 -1
- package/dist/needs-you-live.js +24 -2
- package/dist/needs-you-live.js.map +1 -1
- package/dist/needs-you.d.ts +6 -1
- package/dist/needs-you.js +4 -0
- package/dist/needs-you.js.map +1 -1
- package/dist/outbound-handlers.js +1 -1
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.js +1 -1
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/scheduler/task-board.d.ts +17 -25
- package/dist/scheduler/task-board.js +29 -39
- package/dist/scheduler/task-board.js.map +1 -1
- package/dist/sd-notify.d.ts +13 -14
- package/dist/sd-notify.js +62 -28
- package/dist/sd-notify.js.map +1 -1
- package/dist/settings-api.d.ts +0 -1
- package/dist/settings-api.js +1 -14
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-effect.js +0 -2
- package/dist/settings-effect.js.map +1 -1
- package/dist/setup-wizard.d.ts +3 -0
- package/dist/setup-wizard.js +4 -2
- package/dist/setup-wizard.js.map +1 -1
- package/dist/task-board-waker.d.ts +40 -1
- package/dist/task-board-waker.js +110 -9
- package/dist/task-board-waker.js.map +1 -1
- package/dist/tmux-control.d.ts +1 -1
- package/dist/tmux-control.js +3 -2
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-manager.js +18 -16
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-read.d.ts +2 -0
- package/dist/tmux-read.js +7 -1
- package/dist/tmux-read.js.map +1 -1
- package/dist/topic-commands.d.ts +2 -0
- package/dist/topic-commands.js +2 -1
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-monitor.js +17 -0
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/types.d.ts +7 -10
- package/dist/ui/panel-log.js +2 -2
- package/dist/ui/settings-appearance.js +3 -35
- package/dist/ui/settings-registry.js +0 -1
- package/dist/ui/settings-strings.js +4 -4
- package/dist/ui/settings-web-authority.js +4 -3
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/update-check.d.ts +13 -4
- package/dist/update-check.js +17 -4
- package/dist/update-check.js.map +1 -1
- package/dist/web-experimental.d.ts +6 -2
- package/dist/web-experimental.js +8 -4
- package/dist/web-experimental.js.map +1 -1
- package/dist/web-settings-patch.d.ts +0 -1
- package/dist/web-settings-patch.js +3 -4
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/workflow-templates/default.md +2 -2
- package/dist/working-recovery.d.ts +0 -2
- package/dist/working-recovery.js +2 -23
- package/dist/working-recovery.js.map +1 -1
- package/launcher/agend-install.cjs +7 -1
- package/launcher/fleet-service-owner.cjs +138 -0
- package/package.json +1 -1
- package/dist/task-board-summary.d.ts +0 -101
- package/dist/task-board-summary.js +0 -177
- package/dist/task-board-summary.js.map +0 -1
- package/dist/upgrade-notices.d.ts +0 -17
- package/dist/upgrade-notices.js +0 -93
- package/dist/upgrade-notices.js.map +0 -1
package/dist/fleet-manager.d.ts
CHANGED
|
@@ -362,8 +362,6 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
362
362
|
costGuard: CostGuard | null;
|
|
363
363
|
private statuslineWatcher;
|
|
364
364
|
private dailySummary;
|
|
365
|
-
/** #2246 step 3: the task board's daily summary for the person (task-board-summary.ts). */
|
|
366
|
-
private taskBoardSummary;
|
|
367
365
|
private dailyTipScheduler;
|
|
368
366
|
/** The task board's wakes (step 2): an instance hears when someone else changes a card it owns or handed out. */
|
|
369
367
|
private taskBoardWaker;
|
|
@@ -965,9 +963,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
965
963
|
private listenForWorkingState;
|
|
966
964
|
private observeWorkingState;
|
|
967
965
|
/**
|
|
968
|
-
* #926:
|
|
969
|
-
*
|
|
970
|
-
* notice when the owner is not working and nothing came back in time.
|
|
966
|
+
* #926: idle owners with an unanswered request get one reminder (after the grace). #2802: the only reply notice —
|
|
967
|
+
* requesters are no longer told about overdue or expired asks (they can check delivery_status / describe_instance).
|
|
971
968
|
*/
|
|
972
969
|
sweepReplyObligations(now?: Date): void;
|
|
973
970
|
private expireReplyObligations;
|
|
@@ -2289,13 +2286,6 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
2289
2286
|
* their fleet is organised. Both stay theirs — this only makes sure they are
|
|
2290
2287
|
* not discovered by an agent failing at three in the morning.
|
|
2291
2288
|
*/
|
|
2292
|
-
/**
|
|
2293
|
-
* #1366: the first time a fleet runs 2.2 or later, tell each chat platform's General — once — that its agents can
|
|
2294
|
-
* now be talked to from a browser. Claimed in upgrade-notices.json before the send, released if the send fails.
|
|
2295
|
-
* Platforms with nowhere to post fleet notices (no group, or a Discord fleet with no General channel) are skipped
|
|
2296
|
-
* and not recorded, so they are told once they have one.
|
|
2297
|
-
*/
|
|
2298
|
-
announceWebChatOnce(version: string): Promise<void>;
|
|
2299
2289
|
private announceToolPermissionsChange;
|
|
2300
2290
|
private checkToolPermission;
|
|
2301
2291
|
private handleOutboundFromInstance;
|
|
@@ -2847,13 +2837,6 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
2847
2837
|
* should say so rather than send into a guaranteed failure.
|
|
2848
2838
|
*/
|
|
2849
2839
|
private fleetNoticeTarget;
|
|
2850
|
-
/**
|
|
2851
|
-
* #2246 step 3: the task board's daily summary of [since, until) (task-board-summary.ts). Each chat world's General
|
|
2852
|
-
* gets its own instances' cards — a card that no world's instance holds (nobody's, a person's) goes to the primary
|
|
2853
|
-
* one — and each of that world's Generals shows the same text in the web chat. A world with nothing to say gets
|
|
2854
|
-
* nothing. A web-only fleet: everything, in the web chat.
|
|
2855
|
-
*/
|
|
2856
|
-
private postTaskBoardSummary;
|
|
2857
2840
|
/**
|
|
2858
2841
|
* Post the daily summary where fleet-wide notices go.
|
|
2859
2842
|
*
|
|
@@ -3673,8 +3656,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
3673
3656
|
* The task board's live side: every committed change goes to the open pages (never to a guest) — the board's new
|
|
3674
3657
|
* revision and the card numbers it touched; not who touched them (the page does not need it, and a public-link page
|
|
3675
3658
|
* must not learn a hidden instance's name from it). Its news goes to the instances whose cards someone else changed:
|
|
3676
|
-
* one "[Task board]" notice per instance, gathered for 10 s, held by the delivery outbox until it is taken
|
|
3677
|
-
* (task-board-waker.ts).
|
|
3659
|
+
* one "[Task board]" notice per instance, gathered for 10 s, held by the delivery outbox until it is taken, or
|
|
3660
|
+
* withdrawn once its cards changed again (#2801; task-board-waker.ts).
|
|
3678
3661
|
*/
|
|
3679
3662
|
private attachTaskBoard;
|
|
3680
3663
|
/** A task board notice, as AgEnD's own (not a person's words): through the delivery outbox, held until delivered. */
|
package/dist/fleet-manager.js
CHANGED
|
@@ -52,7 +52,7 @@ import { applyKiroMigration, kiroMigrationPrompt, planKiroMigration } from "./ki
|
|
|
52
52
|
import { fleetLabel } from "./fleet-label.js";
|
|
53
53
|
import { beginFullRestartProgress as persistFullRestartProgress, beginUpdateProgress as persistUpdateProgress, clearUpdateMarker, isUpdateInProgress, readUpdateProgress, sameUpdateMarker, setUpdateProgressStage, updateMarkerIdentity, updateProgressOperation, updateReceiptOf, } from "./update-marker.js";
|
|
54
54
|
import { formatUpdateProgress } from "./update-progress.js";
|
|
55
|
-
import { sdNotify, sdNotifyBlocking, setSdNotifyWarn } from "./sd-notify.js";
|
|
55
|
+
import { sdNotify, sdNotifyBlocking, setSdNotifyWarn, startSdWatchdog } from "./sd-notify.js";
|
|
56
56
|
import { StartupMemoryDeadline, fleetStopConcurrency } from "./startup-memory-deadline.js";
|
|
57
57
|
import { readFleetMemory } from "./process-memory.js";
|
|
58
58
|
import { MemoryPressure } from "./memory-pressure.js";
|
|
@@ -94,13 +94,12 @@ import { processAttachments } from "./channel/attachment-handler.js";
|
|
|
94
94
|
import { routeToolCall } from "./channel/tool-router.js";
|
|
95
95
|
import { Scheduler } from "./scheduler/index.js";
|
|
96
96
|
import { boardActivity, boardCardLimit, boardToolAction } from "./task-board-tool.js";
|
|
97
|
-
import { TaskBoardWaker } from "./task-board-waker.js";
|
|
97
|
+
import { TaskBoardWaker, namesCard } from "./task-board-waker.js";
|
|
98
98
|
import { TaskBoardError, waitsForPerson, wrapUpGap } from "./scheduler/task-board.js";
|
|
99
99
|
import { escapeTelegramHtml, scheduleClock, scheduleRetryLabel } from "./scheduler/retry-label.js";
|
|
100
100
|
import { DEFAULT_SCHEDULER_CONFIG } from "./scheduler/index.js";
|
|
101
101
|
import { TopicCommands, saveCommandForBackend, parseSaveFilename, parsePauseWakeCommand, parseCompactCommand, SAVE_FILENAME_RE, resolveInstanceContext, forgetInstanceContext, readStatuslineJson, statuslineModel } from "./topic-commands.js";
|
|
102
102
|
import { DailySummary } from "./daily-summary.js";
|
|
103
|
-
import { TaskBoardSummaryClock, buildSummary, isQuiet, renderSummary, summaryMarkFile, summaryTime } from "./task-board-summary.js";
|
|
104
103
|
import { WebhookEmitter } from "./webhook-emitter.js";
|
|
105
104
|
import { TmuxControlClient } from "./tmux-control.js";
|
|
106
105
|
import { safeHandler } from "./safe-async.js";
|
|
@@ -254,7 +253,6 @@ import { isSetupComplete, markSetupComplete } from "./setup-marker.js";
|
|
|
254
253
|
import { manualCleanupMessage, reapStaleTunnel } from "./tunnel/lease.js";
|
|
255
254
|
import { buildToolPermissionsNotice } from "./tool-permissions-notice.js";
|
|
256
255
|
import { NeedsYouHub } from "./needs-you-hub.js";
|
|
257
|
-
import { WEB_CHAT_NOTICE, claimNotice, hasWebChat, releaseNotice, upgradeNoticesPath } from "./upgrade-notices.js";
|
|
258
256
|
import { GENERAL_NAME, GENERAL_PAUSE_ERROR, adoptGeneralByName, freeGeneralName, isGeneralInstance } from "./general-instance.js";
|
|
259
257
|
import { buildOrgChart } from "./web-org.js";
|
|
260
258
|
import { topMemoryHogs } from "./memory-hogs.js";
|
|
@@ -612,7 +610,6 @@ export function deriveSpawnConcurrency(freeMemMB, cores) {
|
|
|
612
610
|
/** #926: how often reply obligations are checked, the owner-reminder grace, and the default overdue window. */
|
|
613
611
|
const REPLY_OBLIGATION_SWEEP_MS = 30_000;
|
|
614
612
|
const REPLY_REMINDER_GRACE_MS = 60_000;
|
|
615
|
-
const DEFAULT_REPLY_OVERDUE_MINUTES = 15;
|
|
616
613
|
/**
|
|
617
614
|
* The filter `list_emojis` and `list_stickers` take (#1226). Values may come from the agent CLI as strings, so a
|
|
618
615
|
* limit is read as a number and the flags accept "true". No limit means all of them — never zero.
|
|
@@ -676,32 +673,6 @@ export class FleetManager {
|
|
|
676
673
|
laneFree: name => !this.isInstanceRestarting(name) && !this.activeDurableTargets.has(name)
|
|
677
674
|
&& outbox.countForTarget(name, ["queued", "retry_wait", "delivering", "submission_started", "reconciliation_pending"]) === 0,
|
|
678
675
|
admit: (episode, tasks, advisory) => outbox.admitWorkingRecovery(episode, buildWorkingRecoveryPrompt(tasks, this.workingRecoveryCause, advisory), advisory),
|
|
679
|
-
summary: (group, rows) => {
|
|
680
|
-
const general = this.findGeneralInstance();
|
|
681
|
-
if (!general || !this.daemons.has(general))
|
|
682
|
-
return;
|
|
683
|
-
// Our own informational notice must not supersede General's recovery
|
|
684
|
-
// as unrelated pending work before it crosses the input boundary.
|
|
685
|
-
const generalEpisode = store.get(general);
|
|
686
|
-
if (generalEpisode?.outcome === "admitted" && generalEpisode.deliveryId) {
|
|
687
|
-
const state = outbox.get(generalEpisode.deliveryId)?.state;
|
|
688
|
-
if (!state || !["delivered", "uncertain", "failed", "cancelled"].includes(state))
|
|
689
|
-
return;
|
|
690
|
-
}
|
|
691
|
-
const deferredAdvisories = rows.filter(row => row.outcome === "deferred"
|
|
692
|
-
&& (this.daemons.get(row.instance)?.getWorkingRecoveryState?.().background ?? null) === null)
|
|
693
|
-
.map(row => `${row.instance} (${row.reason})`);
|
|
694
|
-
const posted = store.summary(group, () => outbox.admitSystemNotice(general, `working-recovery-summary:${group}`, `[system:working-recovery-summary] Observed cause: unknown interruption${this.workingRecoveryCause === "unknown_interruption" ? "" : `; prior requested action: ${this.workingRecoveryCause}`}. ${rows.length} interrupted instances; continuation queued ${rows.filter(r => r.outcome === "admitted").length}, excluded ${rows.filter(r => r.outcome === "excluded").length}, still deferred ${rows.filter(r => r.outcome === "deferred").length}. ${rows.filter(r => r.reason === "advisory_background_unknown").length ? `Advisory, background unknown: ${rows.filter(r => r.reason === "advisory_background_unknown").map(r => r.instance).join(", ")}. ` : ""}${deferredAdvisories.length ? `Deferred advisory candidates (background unknown; all other gates still apply): ${deferredAdvisories.join(", ")}. ` : ""}Details are in the Log. Deferred work remains eligible; no action or confirmation is requested.`));
|
|
695
|
-
if (posted) {
|
|
696
|
-
const counts = { interrupted: rows.length, queued: rows.filter(row => row.outcome === "admitted").length,
|
|
697
|
-
excluded: rows.filter(row => row.outcome === "excluded").length, deferred: rows.filter(row => row.outcome === "deferred").length };
|
|
698
|
-
const requestedAction = this.workingRecoveryCause === "unknown_interruption" ? null : this.workingRecoveryCause;
|
|
699
|
-
this.logger.info({ group, ...counts, observedCause: "unknown_interruption", requestedAction }, "Interrupted work recovery summary queued for General");
|
|
700
|
-
this.eventLog?.insert(general, "working_state", { reason: "recovery_summary", group, ...counts,
|
|
701
|
-
observedCause: "unknown_interruption", requestedAction,
|
|
702
|
-
message: `Interrupted work summary queued for General: ${counts.interrupted} interrupted; ${counts.queued} queued, ${counts.excluded} excluded, ${counts.deferred} deferred.` });
|
|
703
|
-
}
|
|
704
|
-
},
|
|
705
676
|
log: (episode, outcome, reason) => {
|
|
706
677
|
const requestedAction = this.workingRecoveryCause === "unknown_interruption" ? null : this.workingRecoveryCause;
|
|
707
678
|
this.logger.info({ name: episode.instance, episode: episode.key, outcome, reason,
|
|
@@ -825,8 +796,6 @@ export class FleetManager {
|
|
|
825
796
|
costGuard = null;
|
|
826
797
|
statuslineWatcher;
|
|
827
798
|
dailySummary = null;
|
|
828
|
-
/** #2246 step 3: the task board's daily summary for the person (task-board-summary.ts). */
|
|
829
|
-
taskBoardSummary = null;
|
|
830
799
|
dailyTipScheduler = null;
|
|
831
800
|
/** The task board's wakes (step 2): an instance hears when someone else changes a card it owns or handed out. */
|
|
832
801
|
taskBoardWaker = null;
|
|
@@ -2436,12 +2405,10 @@ export class FleetManager {
|
|
|
2436
2405
|
const notice = silent
|
|
2437
2406
|
? t(`delivery.fleet_${row.state}_silent_schedule`, row.targetInstance, row.operationId, row.deliveryId, row.lastError ?? "unknown")
|
|
2438
2407
|
: t(`delivery.fleet_${row.state}`, row.targetInstance, row.operationId, row.deliveryId);
|
|
2408
|
+
// #2804: log-only. The person sees a failed or unconfirmed delivery as a Needs-you item (with Acknowledge),
|
|
2409
|
+
// not as a separate General post; the sending agent still gets its delivery-outcome notice.
|
|
2439
2410
|
this.logger.error({ deliveryId: row.deliveryId, operationId: row.operationId, target: row.targetInstance, state: row.state,
|
|
2440
2411
|
cause: row.lastError, ...(silent ? { silentSchedule: true } : {}) }, notice);
|
|
2441
|
-
// A self-scheduled silent heartbeat stays log-only, including the
|
|
2442
|
-
// operator alert; another source still receives the normal notice.
|
|
2443
|
-
if (!silent || row.sourceInstance !== row.targetInstance)
|
|
2444
|
-
this.notifyFleetError(notice);
|
|
2445
2412
|
}
|
|
2446
2413
|
}
|
|
2447
2414
|
});
|
|
@@ -2473,8 +2440,9 @@ export class FleetManager {
|
|
|
2473
2440
|
if (event.count)
|
|
2474
2441
|
this.needsYou?.poke(); // #1386: rows that expired failed — they wait on someone now
|
|
2475
2442
|
this.wakeCoordinator?.kick();
|
|
2443
|
+
// #2804: the expired rows are Needs-you items now; the count is logged, not posted to General.
|
|
2476
2444
|
if (event.count)
|
|
2477
|
-
this.
|
|
2445
|
+
this.logger.warn({ count: event.count, uncertain: event.uncertain ?? 0 }, t("delivery.expired", event.count, event.uncertain ?? 0));
|
|
2478
2446
|
});
|
|
2479
2447
|
this.scheduleDeliveryOutboxPump();
|
|
2480
2448
|
}
|
|
@@ -2604,27 +2572,8 @@ export class FleetManager {
|
|
|
2604
2572
|
const daemon = this.daemons.get(name);
|
|
2605
2573
|
if (daemon?.bootId === daemonBootId)
|
|
2606
2574
|
this.listenForWorkingState(name, daemon);
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
return;
|
|
2610
|
-
for (const prior of outbox.getUnansweredAccepted(name, daemonBootId)) {
|
|
2611
|
-
try {
|
|
2612
|
-
outbox.admitPostRestartOutcomeNotice(prior, daemonBootId);
|
|
2613
|
-
}
|
|
2614
|
-
catch (err) {
|
|
2615
|
-
this.logger.error({ err, source: name, operationId: prior.operationId }, "Could not persist post-restart delivery outcome notice");
|
|
2616
|
-
}
|
|
2617
|
-
}
|
|
2618
|
-
// #926: a restart kills the turn that was working on a request, and a
|
|
2619
|
-
// resumed CLI does not pick it back up. Remind the owner once per restart.
|
|
2620
|
-
try {
|
|
2621
|
-
const now = new Date();
|
|
2622
|
-
this.expireReplyObligations(now);
|
|
2623
|
-
outbox.remindReplyObligations(name, { reason: "restart", graceMs: 0, now, since: now, restartId: daemonBootId });
|
|
2624
|
-
}
|
|
2625
|
-
catch (err) {
|
|
2626
|
-
this.logger.error({ err, owner: name }, "Could not persist the post-restart reply reminder");
|
|
2627
|
-
}
|
|
2575
|
+
// #2802: no post-restart notices. The outbox still delivers or fails a row accepted before the restart, and an open
|
|
2576
|
+
// request is reminded by the ordinary turn-ended nudge (sweepReplyObligations) once the owner is idle.
|
|
2628
2577
|
this.scheduleDeliveryOutboxPump();
|
|
2629
2578
|
}
|
|
2630
2579
|
workingStateListeners;
|
|
@@ -2708,9 +2657,8 @@ export class FleetManager {
|
|
|
2708
2657
|
}
|
|
2709
2658
|
}
|
|
2710
2659
|
/**
|
|
2711
|
-
* #926:
|
|
2712
|
-
*
|
|
2713
|
-
* notice when the owner is not working and nothing came back in time.
|
|
2660
|
+
* #926: idle owners with an unanswered request get one reminder (after the grace). #2802: the only reply notice —
|
|
2661
|
+
* requesters are no longer told about overdue or expired asks (they can check delivery_status / describe_instance).
|
|
2714
2662
|
*/
|
|
2715
2663
|
sweepReplyObligations(now = new Date()) {
|
|
2716
2664
|
const outbox = this.deliveryOutbox;
|
|
@@ -2721,10 +2669,8 @@ export class FleetManager {
|
|
|
2721
2669
|
this.expireReplyObligations(now);
|
|
2722
2670
|
for (const name of this.daemons.keys()) {
|
|
2723
2671
|
if (stateOf(name) === "idle")
|
|
2724
|
-
outbox.remindReplyObligations(name, { now, graceMs: REPLY_REMINDER_GRACE_MS
|
|
2672
|
+
outbox.remindReplyObligations(name, { now, graceMs: REPLY_REMINDER_GRACE_MS });
|
|
2725
2673
|
}
|
|
2726
|
-
const minutes = this.fleetConfig?.defaults?.reply_overdue_minutes ?? DEFAULT_REPLY_OVERDUE_MINUTES;
|
|
2727
|
-
outbox.notifyOverdueReplyObligations({ now, overdueMs: minutes * 60_000, ownerIdle: name => stateOf(name) !== "working" });
|
|
2728
2674
|
}
|
|
2729
2675
|
catch (err) {
|
|
2730
2676
|
this.logger.error({ err }, "Reply-obligation sweep failed");
|
|
@@ -2981,7 +2927,9 @@ export class FleetManager {
|
|
|
2981
2927
|
if ((current?.state === "delivering" || current?.state === "submission_started")
|
|
2982
2928
|
&& current.targetDaemonBootId === claimed.targetDaemonBootId
|
|
2983
2929
|
&& current.managerBootId === claimed.managerBootId) {
|
|
2984
|
-
|
|
2930
|
+
// #2804: logged, not posted. A delivery still in flight needs nobody yet; if it ends failed or unconfirmed it
|
|
2931
|
+
// becomes a Needs-you item.
|
|
2932
|
+
this.logger.warn({ deliveryId, target: claimed.targetInstance }, t("delivery.lane_waiting", claimed.targetInstance, Math.round(DURABLE_DELIVERY_LANE_ALERT_MS / 60_000), deliveryId));
|
|
2985
2933
|
}
|
|
2986
2934
|
}, DURABLE_DELIVERY_LANE_ALERT_MS);
|
|
2987
2935
|
alertTimer.unref?.();
|
|
@@ -8170,7 +8118,7 @@ export class FleetManager {
|
|
|
8170
8118
|
setSdNotifyWarn((fields, message) => this.logger.warn(fields, message));
|
|
8171
8119
|
if (this.watchdogTimer)
|
|
8172
8120
|
clearInterval(this.watchdogTimer);
|
|
8173
|
-
this.watchdogTimer =
|
|
8121
|
+
this.watchdogTimer = startSdWatchdog();
|
|
8174
8122
|
this.eventLoopWatch?.stop();
|
|
8175
8123
|
this.eventLoopWatch = startEventLoopWatch({ logger: this.logger });
|
|
8176
8124
|
try {
|
|
@@ -8346,7 +8294,8 @@ export class FleetManager {
|
|
|
8346
8294
|
...fleet.defaults.daily_summary,
|
|
8347
8295
|
};
|
|
8348
8296
|
this.dailySummary = new DailySummary(summaryConfig, costGuardConfig.timezone, (text) => {
|
|
8349
|
-
|
|
8297
|
+
if (text !== null)
|
|
8298
|
+
this.postDailySummary(text);
|
|
8350
8299
|
// Rotate classic channel chat logs daily
|
|
8351
8300
|
this.classicChannels?.rotateLogs();
|
|
8352
8301
|
this.rotateInboxes();
|
|
@@ -8364,17 +8313,6 @@ export class FleetManager {
|
|
|
8364
8313
|
return DailySummary.generateText(this.eventLog, instances, costMap, this.costGuard?.getFleetTotalCents() ?? 0);
|
|
8365
8314
|
});
|
|
8366
8315
|
this.dailySummary.start();
|
|
8367
|
-
// #2246 step 3: the task board's daily summary — its own time (defaults.task_board.summary_at, read at every look),
|
|
8368
|
-
// in the fleet's zone; a summary missed while the fleet was down goes out when it is back.
|
|
8369
|
-
this.taskBoardSummary = new TaskBoardSummaryClock({
|
|
8370
|
-
at: () => summaryTime(this.fleetConfig?.defaults?.task_board?.summary_at) ?? null,
|
|
8371
|
-
zone: () => this.usageZone(),
|
|
8372
|
-
now: () => new Date(),
|
|
8373
|
-
mark: summaryMarkFile(join(this.dataDir, "task-board-summary.json")),
|
|
8374
|
-
send: (since, until) => this.postTaskBoardSummary(since, until),
|
|
8375
|
-
failed: (err) => this.logger.warn({ err }, "The task board's daily summary failed; the next minute tries again"),
|
|
8376
|
-
});
|
|
8377
|
-
this.taskBoardSummary.start();
|
|
8378
8316
|
// Rotate classic channel chat logs daily (piggyback on daily summary timer)
|
|
8379
8317
|
this.classicChannels?.rotateLogs();
|
|
8380
8318
|
this.rotateInboxes();
|
|
@@ -8734,8 +8672,6 @@ export class FleetManager {
|
|
|
8734
8672
|
}
|
|
8735
8673
|
await this.sendFleetStartCompletionFallback(String(fleet.channel.group_id), text, generalThreadId != null ? String(generalThreadId) : undefined);
|
|
8736
8674
|
}
|
|
8737
|
-
// After "fleet ready", so the notice is not the first thing people see of a restart.
|
|
8738
|
-
void this.announceWebChatOnce(agendVersion);
|
|
8739
8675
|
// #named-link-ttl: a named link with auto_open comes back now — after the web listener and the chat connections.
|
|
8740
8676
|
this.startPublicLinkAutoOpen();
|
|
8741
8677
|
}
|
|
@@ -10870,34 +10806,6 @@ export class FleetManager {
|
|
|
10870
10806
|
* their fleet is organised. Both stay theirs — this only makes sure they are
|
|
10871
10807
|
* not discovered by an agent failing at three in the morning.
|
|
10872
10808
|
*/
|
|
10873
|
-
/**
|
|
10874
|
-
* #1366: the first time a fleet runs 2.2 or later, tell each chat platform's General — once — that its agents can
|
|
10875
|
-
* now be talked to from a browser. Claimed in upgrade-notices.json before the send, released if the send fails.
|
|
10876
|
-
* Platforms with nowhere to post fleet notices (no group, or a Discord fleet with no General channel) are skipped
|
|
10877
|
-
* and not recorded, so they are told once they have one.
|
|
10878
|
-
*/
|
|
10879
|
-
async announceWebChatOnce(version) {
|
|
10880
|
-
if (!hasWebChat(version))
|
|
10881
|
-
return;
|
|
10882
|
-
const path = upgradeNoticesPath(this.dataDir);
|
|
10883
|
-
for (const [adapterId, world] of this.worlds) {
|
|
10884
|
-
if (this.shuttingDown)
|
|
10885
|
-
return;
|
|
10886
|
-
const target = this.fleetNoticeTarget(adapterId);
|
|
10887
|
-
if (!target)
|
|
10888
|
-
continue;
|
|
10889
|
-
if (!claimNotice(path, WEB_CHAT_NOTICE, adapterId))
|
|
10890
|
-
continue;
|
|
10891
|
-
try {
|
|
10892
|
-
await world.adapter.sendText(target.chatId, t("upgrade.web_chat"), target.opts);
|
|
10893
|
-
this.logger.info({ adapterId, notice: WEB_CHAT_NOTICE }, "Announced web chat in General");
|
|
10894
|
-
}
|
|
10895
|
-
catch (err) {
|
|
10896
|
-
const released = releaseNotice(path, WEB_CHAT_NOTICE, adapterId);
|
|
10897
|
-
this.logger.warn({ err, adapterId, released }, "Could not announce web chat in General — will try at the next start");
|
|
10898
|
-
}
|
|
10899
|
-
}
|
|
10900
|
-
}
|
|
10901
10809
|
announceToolPermissionsChange() {
|
|
10902
10810
|
try {
|
|
10903
10811
|
const thirtyDaysAgo = new Date(Date.now() - 30 * 864e5).toISOString().slice(0, 19).replace("T", " ");
|
|
@@ -14392,51 +14300,6 @@ export class FleetManager {
|
|
|
14392
14300
|
}
|
|
14393
14301
|
return { chatId, opts: {} };
|
|
14394
14302
|
}
|
|
14395
|
-
/**
|
|
14396
|
-
* #2246 step 3: the task board's daily summary of [since, until) (task-board-summary.ts). Each chat world's General
|
|
14397
|
-
* gets its own instances' cards — a card that no world's instance holds (nobody's, a person's) goes to the primary
|
|
14398
|
-
* one — and each of that world's Generals shows the same text in the web chat. A world with nothing to say gets
|
|
14399
|
-
* nothing. A web-only fleet: everything, in the web chat.
|
|
14400
|
-
*/
|
|
14401
|
-
postTaskBoardSummary(since, until) {
|
|
14402
|
-
const board = this.scheduler?.db.board;
|
|
14403
|
-
if (!board)
|
|
14404
|
-
return;
|
|
14405
|
-
const read = board.summaryRead(since.toISOString(), until.toISOString(), this.dispatcherGone);
|
|
14406
|
-
const projects = board.listProjects(true);
|
|
14407
|
-
const stats = board.projectStats(this.dispatcherGone);
|
|
14408
|
-
const names = new Map(projects.map((p) => [p.id, p.name]));
|
|
14409
|
-
const configured = this.getPrimaryAdapterId();
|
|
14410
|
-
const primary = configured !== undefined && this.worlds.has(configured) ? configured : this.worlds.keys().next().value;
|
|
14411
|
-
const worldOf = (assignee) => {
|
|
14412
|
-
const owner = assignee ? this.getInstanceAdapterId(assignee) : undefined;
|
|
14413
|
-
return owner !== undefined && this.worlds.has(owner) ? owner : primary;
|
|
14414
|
-
};
|
|
14415
|
-
const zone = this.usageZone();
|
|
14416
|
-
for (const world of this.worlds.size ? [...this.worlds.keys()] : [undefined]) {
|
|
14417
|
-
const summary = buildSummary(read, since, until, { inChat: (assignee) => worldOf(assignee) === world, projects, stats });
|
|
14418
|
-
if (isQuiet(summary))
|
|
14419
|
-
continue;
|
|
14420
|
-
const text = renderSummary(summary, { zone, t: (key, ...args) => t(key, ...args), projectOf: (c) => (c.projectId === null ? null : names.get(c.projectId) ?? null) });
|
|
14421
|
-
const cards = [...new Set([...summary.review, ...summary.blocked, ...summary.done, ...summary.created].map((c) => c.code))];
|
|
14422
|
-
if (world !== undefined) {
|
|
14423
|
-
const adapter = this.worlds.get(world)?.adapter;
|
|
14424
|
-
const target = this.fleetNoticeTarget(world);
|
|
14425
|
-
if (adapter && target) {
|
|
14426
|
-
adapter.sendText(target.chatId, text, target.opts).catch(e => this.logger.warn({ err: e, world }, "Failed to send the task board's daily summary"));
|
|
14427
|
-
}
|
|
14428
|
-
else {
|
|
14429
|
-
this.logger.warn({ world }, "The task board's daily summary has no postable target — set a General topic or channel.options.general_channel_id");
|
|
14430
|
-
}
|
|
14431
|
-
}
|
|
14432
|
-
for (const [name, cfg] of Object.entries(this.fleetConfig?.instances ?? {})) {
|
|
14433
|
-
if (cfg.general_topic !== true || (world !== undefined && this.getInstanceAdapterId(name) !== world))
|
|
14434
|
-
continue;
|
|
14435
|
-
// #2341 review: with the cards it names, so the public link withholds it while one of them is hidden there.
|
|
14436
|
-
this.emitSseEvent("message", { instance: name, sender: name, role: "status", text, ts: new Date().toISOString(), cards });
|
|
14437
|
-
}
|
|
14438
|
-
}
|
|
14439
|
-
}
|
|
14440
14303
|
/**
|
|
14441
14304
|
* Post the daily summary where fleet-wide notices go.
|
|
14442
14305
|
*
|
|
@@ -15146,6 +15009,7 @@ export class FleetManager {
|
|
|
15146
15009
|
pauseReason, pausedAt,
|
|
15147
15010
|
crashedAt: crashed ? (this.needsCrashedAt.get(name) ?? null) : null,
|
|
15148
15011
|
signIn: pauseReason === "auth" ? this.signInFor(name) : null,
|
|
15012
|
+
usageHold: this.daemons.get(name)?.usageHold?.() ?? null,
|
|
15149
15013
|
});
|
|
15150
15014
|
}
|
|
15151
15015
|
return out;
|
|
@@ -18321,8 +18185,8 @@ export class FleetManager {
|
|
|
18321
18185
|
* The task board's live side: every committed change goes to the open pages (never to a guest) — the board's new
|
|
18322
18186
|
* revision and the card numbers it touched; not who touched them (the page does not need it, and a public-link page
|
|
18323
18187
|
* must not learn a hidden instance's name from it). Its news goes to the instances whose cards someone else changed:
|
|
18324
|
-
* one "[Task board]" notice per instance, gathered for 10 s, held by the delivery outbox until it is taken
|
|
18325
|
-
* (task-board-waker.ts).
|
|
18188
|
+
* one "[Task board]" notice per instance, gathered for 10 s, held by the delivery outbox until it is taken, or
|
|
18189
|
+
* withdrawn once its cards changed again (#2801; task-board-waker.ts).
|
|
18326
18190
|
*/
|
|
18327
18191
|
attachTaskBoard(db) {
|
|
18328
18192
|
this.taskBoardWaker?.stop();
|
|
@@ -18331,6 +18195,19 @@ export class FleetManager {
|
|
|
18331
18195
|
const waker = this.taskBoardWaker = new TaskBoardWaker({
|
|
18332
18196
|
deliver: (instance, message, key) => this.admitTaskBoardNotice(instance, message, key),
|
|
18333
18197
|
isInstance: (name) => this.fleetConfig?.instances?.[name] != null,
|
|
18198
|
+
// #2785 (c), #2801: the maker's own message already told it; the board's line would repeat it.
|
|
18199
|
+
messagedAbout: (from, to, codes, sinceMs) => {
|
|
18200
|
+
const outbox = this.deliveryOutbox;
|
|
18201
|
+
if (!outbox?.isOpen)
|
|
18202
|
+
return false;
|
|
18203
|
+
return outbox.recentTextsFrom(to, from, new Date(sinceMs).toISOString()).some((text) => codes.some((code) => namesCard(text, code)));
|
|
18204
|
+
},
|
|
18205
|
+
// #2801: a notice still waiting whose cards have all changed again is not delivered.
|
|
18206
|
+
withdraw: (prefix, stale, reason) => {
|
|
18207
|
+
const outbox = this.deliveryOutbox;
|
|
18208
|
+
return outbox?.isOpen ? outbox.withdrawWaitingNotices(prefix, stale, reason).length : 0;
|
|
18209
|
+
},
|
|
18210
|
+
changedAt: (uuid) => db.board.lastChangeAt(uuid),
|
|
18334
18211
|
holds: (wake) => db.board.wakeHolds(wake),
|
|
18335
18212
|
card: (uuid) => db.board.findCard(uuid),
|
|
18336
18213
|
// #2352: the card's Needs you buttons are in this instance's own channel (it is the card's lane).
|
|
@@ -18347,12 +18224,13 @@ export class FleetManager {
|
|
|
18347
18224
|
},
|
|
18348
18225
|
log: (level, message, extra) => this.logger[level](extra ?? {}, message),
|
|
18349
18226
|
});
|
|
18350
|
-
db.board.onChange(({ rev, ids, projects, wakes }) => {
|
|
18227
|
+
db.board.onChange(({ rev, ids, projects, wakes, changed }) => {
|
|
18351
18228
|
// Refresh both old and new owners on reassignment; wakes are not an ownership index.
|
|
18352
18229
|
if (ids.length)
|
|
18353
18230
|
for (const name of this.daemons.keys())
|
|
18354
18231
|
this.observeWorkingState(name, "tasks");
|
|
18355
18232
|
this.emitSseEvent("tasks", { rev, ids, projects });
|
|
18233
|
+
waker.changed(changed);
|
|
18356
18234
|
waker.note(wakes);
|
|
18357
18235
|
// step 3: cards waiting for the person are Needs you items; #2355 review: a project's lead routes them too.
|
|
18358
18236
|
if (ids.length || projects)
|
|
@@ -21399,7 +21277,6 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
21399
21277
|
this.clearStatuslineWatchers();
|
|
21400
21278
|
this.costGuard?.stop();
|
|
21401
21279
|
this.dailySummary?.stop();
|
|
21402
|
-
this.taskBoardSummary?.stop();
|
|
21403
21280
|
this.dailyTipScheduler?.stop();
|
|
21404
21281
|
this.dailyTipScheduler = null;
|
|
21405
21282
|
if (this.updateCheckTimer) {
|
|
@@ -23506,7 +23383,9 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
23506
23383
|
// From the install's own channel tag, this is what /update installs — even a stable the release pointed the
|
|
23507
23384
|
// tag at (2.2.0 on @alpha / @beta): the plain /update text, never "/update stays on … use --stable" (#1928).
|
|
23508
23385
|
const key = fromChannel ? "update.available_current" : updateNoticeKey(currentVersion, target);
|
|
23509
|
-
|
|
23386
|
+
// #2775: null = a beta install and a stable target; beta users are never told to switch.
|
|
23387
|
+
if (key)
|
|
23388
|
+
this.notifyInstanceTopic(generalId, t(key, `v${target}`, `v${currentVersion}`));
|
|
23510
23389
|
}
|
|
23511
23390
|
}
|
|
23512
23391
|
}
|