@songsid/agend 2.1.7-beta.4 → 2.1.7-beta.6
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 +2 -0
- package/README.zh-TW.md +2 -0
- package/dist/agent-cli-instructions.md +5 -0
- package/dist/agent-cli.js +13 -0
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +1 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/backend/codex-session-lookup.d.ts +59 -0
- package/dist/backend/codex-session-lookup.js +139 -0
- package/dist/backend/codex-session-lookup.js.map +1 -0
- package/dist/backend/codex.d.ts +37 -9
- package/dist/backend/codex.js +204 -38
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/grok.js +51 -0
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/types.d.ts +12 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/mcp-tools.js +2 -0
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/cli.js +137 -124
- package/dist/cli.js.map +1 -1
- package/dist/completion-install.d.ts +32 -0
- package/dist/completion-install.js +125 -1
- package/dist/completion-install.js.map +1 -1
- package/dist/config-validator.js +4 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.d.ts +12 -1
- package/dist/daemon.js +186 -27
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +199 -6
- package/dist/delivery-outbox.js +602 -44
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-outbox.test.js +268 -14
- package/dist/delivery-outbox.test.js.map +1 -1
- package/dist/delivery-queue-evidence.d.ts +15 -0
- package/dist/delivery-queue-evidence.js +35 -0
- package/dist/delivery-queue-evidence.js.map +1 -0
- package/dist/delivery-queue-evidence.test.d.ts +1 -0
- package/dist/delivery-queue-evidence.test.js +23 -0
- package/dist/delivery-queue-evidence.test.js.map +1 -0
- package/dist/delivery-reconciliation.d.ts +49 -0
- package/dist/delivery-reconciliation.js +296 -0
- package/dist/delivery-reconciliation.js.map +1 -0
- package/dist/delivery-reconciliation.test.d.ts +1 -0
- package/dist/delivery-reconciliation.test.js +411 -0
- package/dist/delivery-reconciliation.test.js.map +1 -0
- package/dist/fleet-manager.d.ts +10 -1
- package/dist/fleet-manager.js +109 -32
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +10 -0
- package/dist/instance-lifecycle.d.ts +13 -0
- package/dist/instance-lifecycle.js +105 -21
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +6 -0
- package/dist/instructions.js +9 -0
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +8 -0
- package/dist/locale.js.map +1 -1
- package/dist/ls-rows.d.ts +50 -0
- package/dist/ls-rows.js +152 -0
- package/dist/ls-rows.js.map +1 -0
- package/dist/outbound-handlers.d.ts +3 -0
- package/dist/outbound-handlers.js +38 -2
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +8 -0
- package/dist/outbound-schemas.js +15 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/pane-write-lock.d.ts +2 -0
- package/dist/pane-write-lock.js +19 -0
- package/dist/pane-write-lock.js.map +1 -1
- package/dist/quickstart.js +9 -3
- package/dist/quickstart.js.map +1 -1
- package/dist/remote-credential-check.d.ts +26 -7
- package/dist/remote-credential-check.js +157 -10
- package/dist/remote-credential-check.js.map +1 -1
- package/dist/scheduler/scheduler.d.ts +5 -1
- package/dist/scheduler/scheduler.js +60 -20
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/scheduler/scheduler.test.js +41 -2
- package/dist/scheduler/scheduler.test.js.map +1 -1
- package/dist/scheduler/types.d.ts +1 -1
- package/dist/tmux-manager.d.ts +3 -3
- package/dist/tmux-manager.js +6 -6
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tool-permissions.js +6 -6
- package/dist/tool-permissions.js.map +1 -1
- package/dist/transcript-monitor.d.ts +3 -1
- package/dist/transcript-monitor.js +15 -0
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +9 -0
- package/dist/transcript-sources.js +14 -0
- package/dist/transcript-sources.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/ui/dashboard.html +7 -5
- package/dist/ui/view.html +138 -45
- package/dist/usage/usage-api.js +30 -0
- package/dist/usage/usage-api.js.map +1 -1
- package/package.json +1 -1
package/dist/fleet-manager.js
CHANGED
|
@@ -286,6 +286,10 @@ export function deriveSpawnConcurrency(freeMemMB, cores) {
|
|
|
286
286
|
const byMemory = Math.floor(freeMemMB / 300);
|
|
287
287
|
return Math.max(2, Math.min(10, byMemory, Math.max(1, cores)));
|
|
288
288
|
}
|
|
289
|
+
/** #926: how often reply obligations are checked, the owner-reminder grace, and the default overdue window. */
|
|
290
|
+
const REPLY_OBLIGATION_SWEEP_MS = 30_000;
|
|
291
|
+
const REPLY_REMINDER_GRACE_MS = 60_000;
|
|
292
|
+
const DEFAULT_REPLY_OVERDUE_MINUTES = 15;
|
|
289
293
|
export class FleetManager {
|
|
290
294
|
dataDir;
|
|
291
295
|
static signalTarget = null;
|
|
@@ -303,6 +307,8 @@ export class FleetManager {
|
|
|
303
307
|
deliveryPumpScheduled = false;
|
|
304
308
|
deliveryPumpRunning = false;
|
|
305
309
|
deliveryPumpTimer = null;
|
|
310
|
+
/** #926: periodic reply-obligation sweep (owner reminders, requester overdue notices). */
|
|
311
|
+
replyObligationTimer = null;
|
|
306
312
|
activeDurableTargets = new Set();
|
|
307
313
|
/** Live view of lifecycle.daemons — used throughout; not deprecated. */
|
|
308
314
|
get daemons() { return this.lifecycle.daemons; }
|
|
@@ -580,9 +586,6 @@ export class FleetManager {
|
|
|
580
586
|
if (!this.deliveryOutboxRecovered) {
|
|
581
587
|
const recovered = outbox.recoverForBoot(this.managerBootId);
|
|
582
588
|
this.logger.info({ ...recovered }, "Recovered durable delivery outbox for this process boot");
|
|
583
|
-
if (recovered.uncertain > 0) {
|
|
584
|
-
this.notifyFleetError(`${recovered.uncertain} durable delivery outcome(s) became uncertain during process restart; inspect the sender's delivery notice before retrying.`);
|
|
585
|
-
}
|
|
586
589
|
this.deliveryOutboxRecovered = true;
|
|
587
590
|
}
|
|
588
591
|
outbox.on("admitted", () => this.scheduleDeliveryOutboxPump());
|
|
@@ -598,10 +601,18 @@ export class FleetManager {
|
|
|
598
601
|
}
|
|
599
602
|
});
|
|
600
603
|
outbox.on("generation_recovered", () => this.scheduleDeliveryOutboxPump());
|
|
604
|
+
this.replyObligationTimer = setInterval(() => this.sweepReplyObligations(), REPLY_OBLIGATION_SWEEP_MS);
|
|
605
|
+
this.replyObligationTimer.unref?.();
|
|
606
|
+
// #856: the text the target daemon received differs from what was
|
|
607
|
+
// admitted. Transport has never been seen to do this; a warning and the
|
|
608
|
+
// attempt row's flag are the whole response until it has.
|
|
609
|
+
outbox.on("content_digest_mismatch", (event) => {
|
|
610
|
+
this.logger.warn(event, "Delivered text differs from the admitted text (content digest mismatch)");
|
|
611
|
+
});
|
|
601
612
|
outbox.on("expired", (event) => {
|
|
602
613
|
this.scheduleDeliveryOutboxPump();
|
|
603
614
|
if (event.count)
|
|
604
|
-
this.notifyFleetError(`${event.count} durable delivery row(s)
|
|
615
|
+
this.notifyFleetError(`${event.count} durable delivery row(s) reached the 24-hour outbox bound; ${event.uncertain ?? 0} unresolved submissions are uncertain and sender notices were queued.`);
|
|
605
616
|
});
|
|
606
617
|
this.scheduleDeliveryOutboxPump();
|
|
607
618
|
}
|
|
@@ -632,6 +643,10 @@ export class FleetManager {
|
|
|
632
643
|
duplicate: !admitted.inserted,
|
|
633
644
|
};
|
|
634
645
|
}
|
|
646
|
+
queryDurableDeliveryStatus(callerInstance, selector) {
|
|
647
|
+
this.ensureDeliveryOutbox();
|
|
648
|
+
return this.deliveryOutbox.queryStatusForInstance(callerInstance, selector);
|
|
649
|
+
}
|
|
635
650
|
getDaemonBootId(instanceName) {
|
|
636
651
|
return this.daemons.get(instanceName)?.bootId;
|
|
637
652
|
}
|
|
@@ -651,8 +666,38 @@ export class FleetManager {
|
|
|
651
666
|
this.logger.error({ err, source: name, operationId: prior.operationId }, "Could not persist post-restart delivery outcome notice");
|
|
652
667
|
}
|
|
653
668
|
}
|
|
669
|
+
// #926: a restart kills the turn that was working on a request, and a
|
|
670
|
+
// resumed CLI does not pick it back up. Remind the owner once per restart.
|
|
671
|
+
try {
|
|
672
|
+
outbox.remindReplyObligations(name, { reason: "restart", graceMs: 0, since: new Date() });
|
|
673
|
+
}
|
|
674
|
+
catch (err) {
|
|
675
|
+
this.logger.error({ err, owner: name }, "Could not persist the post-restart reply reminder");
|
|
676
|
+
}
|
|
654
677
|
this.scheduleDeliveryOutboxPump();
|
|
655
678
|
}
|
|
679
|
+
/**
|
|
680
|
+
* #926: the two reply-obligation safety nets. Idle owners with an unanswered
|
|
681
|
+
* request get one reminder (after the grace); requesters get one overdue
|
|
682
|
+
* notice when the owner is not working and nothing came back in time.
|
|
683
|
+
*/
|
|
684
|
+
sweepReplyObligations(now = new Date()) {
|
|
685
|
+
const outbox = this.deliveryOutbox;
|
|
686
|
+
if (!outbox || this.shuttingDown)
|
|
687
|
+
return;
|
|
688
|
+
const stateOf = (name) => this.daemons.get(name)?.getInstanceState();
|
|
689
|
+
try {
|
|
690
|
+
for (const name of this.daemons.keys()) {
|
|
691
|
+
if (stateOf(name) === "idle")
|
|
692
|
+
outbox.remindReplyObligations(name, { now, graceMs: REPLY_REMINDER_GRACE_MS, reason: "turn-ended" });
|
|
693
|
+
}
|
|
694
|
+
const minutes = this.fleetConfig?.defaults?.reply_overdue_minutes ?? DEFAULT_REPLY_OVERDUE_MINUTES;
|
|
695
|
+
outbox.notifyOverdueReplyObligations({ now, overdueMs: minutes * 60_000, ownerIdle: name => stateOf(name) !== "working" });
|
|
696
|
+
}
|
|
697
|
+
catch (err) {
|
|
698
|
+
this.logger.error({ err }, "Reply-obligation sweep failed");
|
|
699
|
+
}
|
|
700
|
+
}
|
|
656
701
|
scheduleDeliveryOutboxPump(delayMs = 0) {
|
|
657
702
|
if (!this.deliveryOutbox || this.shuttingDown)
|
|
658
703
|
return;
|
|
@@ -716,26 +761,42 @@ export class FleetManager {
|
|
|
716
761
|
const target = claimed.targetInstance;
|
|
717
762
|
const attempt = claimed.attemptNo;
|
|
718
763
|
const payload = { ...claimed.payload };
|
|
764
|
+
const laneReleased = this.waitForDurableLaneRelease(claimed);
|
|
719
765
|
const rawMeta = payload.meta && typeof payload.meta === "object"
|
|
720
766
|
? payload.meta
|
|
721
767
|
: {};
|
|
722
|
-
payload.meta = {
|
|
723
|
-
...rawMeta,
|
|
724
|
-
delivery_id: claimed.deliveryId,
|
|
725
|
-
delivery_attempt: String(claimed.attemptNo),
|
|
726
|
-
from_instance: typeof rawMeta.from_instance === "string" && rawMeta.from_instance
|
|
727
|
-
? rawMeta.from_instance
|
|
728
|
-
: claimed.sourceInstance,
|
|
729
|
-
correlation_id: claimed.correlationId ?? String(rawMeta.correlation_id ?? ""),
|
|
730
|
-
};
|
|
731
768
|
// Arm the visible liveness alert as soon as this generation owns the lane,
|
|
732
769
|
// including time spent waiting for a handoff/idle gate to return.
|
|
733
|
-
const laneReleased = this.waitForDurableLaneRelease(claimed);
|
|
734
770
|
try {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
771
|
+
let sent;
|
|
772
|
+
if (claimed.kind === "raw_paste") {
|
|
773
|
+
if (payload.type !== "raw_paste" || typeof payload.content !== "string") {
|
|
774
|
+
throw new Error("Durable raw_paste row has an invalid payload");
|
|
775
|
+
}
|
|
776
|
+
// Preserve the schedule's exact bytes and raw route. The target daemon
|
|
777
|
+
// owns readiness, paste, one Enter, and the fenced outbox transition.
|
|
778
|
+
sent = await this.deliverToInstance(target, {
|
|
779
|
+
type: "raw_paste",
|
|
780
|
+
content: payload.content,
|
|
781
|
+
delivery_id: claimed.deliveryId,
|
|
782
|
+
delivery_attempt: String(claimed.attemptNo),
|
|
783
|
+
}, { waitForIdle: false });
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
payload.meta = {
|
|
787
|
+
...rawMeta,
|
|
788
|
+
delivery_id: claimed.deliveryId,
|
|
789
|
+
delivery_attempt: String(claimed.attemptNo),
|
|
790
|
+
from_instance: typeof rawMeta.from_instance === "string" && rawMeta.from_instance
|
|
791
|
+
? rawMeta.from_instance
|
|
792
|
+
: claimed.sourceInstance,
|
|
793
|
+
correlation_id: claimed.correlationId ?? String(rawMeta.correlation_id ?? ""),
|
|
794
|
+
};
|
|
795
|
+
sent = await this.deliverToInstance(target, payload, {
|
|
796
|
+
isCrossInstance: true,
|
|
797
|
+
waitForIdle: claimed.kind !== "steer",
|
|
798
|
+
});
|
|
799
|
+
}
|
|
739
800
|
if (!sent) {
|
|
740
801
|
const current = outbox.get(claimed.deliveryId);
|
|
741
802
|
if (current?.state === "delivering") {
|
|
@@ -3043,7 +3104,7 @@ export class FleetManager {
|
|
|
3043
3104
|
...DEFAULT_SCHEDULER_CONFIG,
|
|
3044
3105
|
...this.fleetConfig?.defaults.scheduler,
|
|
3045
3106
|
};
|
|
3046
|
-
this.scheduler = new Scheduler(join(this.dataDir, "scheduler.db"), (schedule) => this.handleScheduleTrigger(schedule), schedulerConfig, (name) => this.fleetConfig?.instances?.[name] != null || !!this.classicChannels?.getAll().some(ch => ch.instanceName === name));
|
|
3107
|
+
this.scheduler = new Scheduler(join(this.dataDir, "scheduler.db"), (schedule, runId) => this.handleScheduleTrigger(schedule, runId), schedulerConfig, (name) => this.fleetConfig?.instances?.[name] != null || !!this.classicChannels?.getAll().some(ch => ch.instanceName === name));
|
|
3047
3108
|
this.scheduler.init();
|
|
3048
3109
|
this.logger.info("Scheduler initialized");
|
|
3049
3110
|
// Tips share the daily-report clock but remain an independent internal
|
|
@@ -5600,7 +5661,7 @@ export class FleetManager {
|
|
|
5600
5661
|
}
|
|
5601
5662
|
}
|
|
5602
5663
|
// ===================== Scheduler =====================
|
|
5603
|
-
async handleScheduleTrigger(schedule) {
|
|
5664
|
+
async handleScheduleTrigger(schedule, stableRunId = randomUUID()) {
|
|
5604
5665
|
const { target, reply_chat_id, reply_thread_id, message, label, id, source, silent } = schedule;
|
|
5605
5666
|
const RATE_LIMIT_DEFER_THRESHOLD = 85;
|
|
5606
5667
|
const rl = this.statuslineWatcher.getRateLimits(target);
|
|
@@ -5618,20 +5679,32 @@ export class FleetManager {
|
|
|
5618
5679
|
}
|
|
5619
5680
|
// Silent mode: paste directly to tmux pane — no channel message.
|
|
5620
5681
|
if (silent) {
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5682
|
+
// The outbox commit is the schedule's durable acceptance point. The run
|
|
5683
|
+
// key is the scheduled fire instant (or an invocation UUID for a manual
|
|
5684
|
+
// trigger), so restart/catch-up reuses the same raw paste row.
|
|
5685
|
+
this.ensureDeliveryOutbox();
|
|
5686
|
+
const runKey = encodeURIComponent(stableRunId);
|
|
5687
|
+
const sourceKey = `schedule:${encodeURIComponent(id)}:${runKey}:${encodeURIComponent(target)}:raw_paste`;
|
|
5688
|
+
const operationId = `schedule:${id}:${stableRunId}`;
|
|
5689
|
+
const sourceBootId = this.daemons.get(source)?.bootId ?? this.managerBootId;
|
|
5690
|
+
const admitted = this.deliveryOutbox.admit({
|
|
5691
|
+
operationId,
|
|
5692
|
+
sourceKey,
|
|
5693
|
+
sourceInstance: source,
|
|
5694
|
+
sourceDaemonBootId: sourceBootId,
|
|
5695
|
+
targetInstance: target,
|
|
5696
|
+
kind: "raw_paste",
|
|
5697
|
+
correlationId: operationId,
|
|
5698
|
+
payload: {
|
|
5624
5699
|
type: "raw_paste",
|
|
5625
5700
|
content: message,
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
}
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
this.logger.warn({ target, scheduleId: id }, "Silent schedule: IPC not connected, skipping");
|
|
5634
|
-
}
|
|
5701
|
+
schedule_id: id,
|
|
5702
|
+
schedule_run_id: stableRunId,
|
|
5703
|
+
},
|
|
5704
|
+
});
|
|
5705
|
+
this.scheduler.recordRun(id, "queued", `durable raw_paste delivery_id=${admitted.delivery.deliveryId}`);
|
|
5706
|
+
this.logger.info({ target, scheduleId: id, runId: stableRunId, deliveryId: admitted.delivery.deliveryId, duplicate: !admitted.inserted }, "Silent schedule durably admitted as raw_paste");
|
|
5707
|
+
this.scheduleDeliveryOutboxPump();
|
|
5635
5708
|
return;
|
|
5636
5709
|
}
|
|
5637
5710
|
const schedulerDefaults = this.fleetConfig?.defaults.scheduler;
|
|
@@ -11383,6 +11456,10 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
11383
11456
|
clearInterval(this.eventLogPruneTimer);
|
|
11384
11457
|
this.eventLogPruneTimer = null;
|
|
11385
11458
|
}
|
|
11459
|
+
if (this.replyObligationTimer) {
|
|
11460
|
+
clearInterval(this.replyObligationTimer);
|
|
11461
|
+
this.replyObligationTimer = null;
|
|
11462
|
+
}
|
|
11386
11463
|
if (this.logRotateTimer) {
|
|
11387
11464
|
clearInterval(this.logRotateTimer);
|
|
11388
11465
|
this.logRotateTimer = null;
|