@songsid/agend 2.2.0-alpha.9 → 2.2.0-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/README.md +1 -1
- package/README.zh-TW.md +1 -1
- package/dist/agent-cli-instructions.md +10 -7
- package/dist/agent-endpoint.js +21 -9
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-whats-new.d.ts +47 -0
- package/dist/agent-whats-new.js +141 -0
- package/dist/agent-whats-new.js.map +1 -0
- package/dist/auth-api.d.ts +2 -2
- package/dist/auth-api.js +2 -2
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/kiro-engine-ledger.d.ts +5 -0
- package/dist/backend/kiro-engine-ledger.js +20 -0
- package/dist/backend/kiro-engine-ledger.js.map +1 -1
- package/dist/backend/kiro-identity.d.ts +6 -0
- package/dist/backend/kiro-identity.js +19 -5
- package/dist/backend/kiro-identity.js.map +1 -1
- package/dist/backend/kiro.d.ts +14 -2
- package/dist/backend/kiro.js +84 -9
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +4 -2
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +13 -1
- package/dist/channel/adapters/discord.js +34 -2
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +11 -1
- package/dist/channel/adapters/telegram.js +28 -2
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-server.js +9 -1
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.d.ts +11 -0
- package/dist/channel/mcp-tools.js +23 -12
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/tool-router.js +7 -2
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/channel/types.d.ts +13 -1
- package/dist/cli.js +55 -16
- package/dist/cli.js.map +1 -1
- package/dist/command-table.js +1 -1
- package/dist/command-table.js.map +1 -1
- package/dist/config-validator.js +48 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/cross-instance-envelope.d.ts +15 -2
- package/dist/cross-instance-envelope.js +27 -5
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +36 -0
- package/dist/daemon.js +157 -6
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +6 -0
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/fleet-manager.d.ts +48 -0
- package/dist/fleet-manager.js +275 -6
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/backend-providers/SKILL.md +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +9 -6
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +11 -2
- package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +8 -8
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +5 -5
- package/dist/general-knowledge/skills/fleet-health/SKILL.md +4 -4
- package/dist/general-knowledge/skills/fleet-restart/SKILL.md +13 -13
- package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +3 -2
- package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +6 -0
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +9 -9
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +8 -2
- package/dist/general-knowledge/skills/scheduling/SKILL.md +5 -3
- package/dist/general-knowledge/skills/session-management/SKILL.md +6 -5
- package/dist/general-knowledge/skills/tui-effort/SKILL.md +2 -2
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +4 -3
- package/dist/general-knowledge/steering/core-rules.md +8 -7
- package/dist/instance-avatar.d.ts +36 -0
- package/dist/instance-avatar.js +57 -0
- package/dist/instance-avatar.js.map +1 -0
- package/dist/instance-config-impact.js +5 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +4 -0
- package/dist/instance-lifecycle.js +3 -1
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +2 -0
- package/dist/instructions.js +10 -7
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +36 -26
- package/dist/locale.js.map +1 -1
- package/dist/next-steps.js +1 -1
- package/dist/next-steps.js.map +1 -1
- package/dist/outbound-handlers.js +7 -4
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +8 -0
- package/dist/outbound-schemas.js +15 -5
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +3 -0
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/service-activation.js +4 -3
- package/dist/service-activation.js.map +1 -1
- package/dist/settings-api.js +71 -21
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +70 -1
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +26 -4
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.js +7 -0
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +1 -1
- package/dist/status-emojis.d.ts +10 -3
- package/dist/status-emojis.js +11 -2
- package/dist/status-emojis.js.map +1 -1
- package/dist/tips.js +4 -4
- package/dist/tips.js.map +1 -1
- package/dist/transcript-monitor.d.ts +8 -0
- package/dist/transcript-monitor.js +26 -2
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +54 -0
- package/dist/transcript-sources.js +188 -27
- package/dist/transcript-sources.js.map +1 -1
- package/dist/transcript-turns.d.ts +15 -2
- package/dist/transcript-turns.js +15 -2
- package/dist/transcript-turns.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/ui/chat-emojis.js +33 -0
- package/dist/ui/chat-render.js +12 -0
- package/dist/ui/chat-thread.js +1 -1
- package/dist/ui/panel-chat.js +13 -3
- package/dist/ui/panel-needs.js +5 -2
- package/dist/ui/panel-settings.js +117 -26
- package/dist/ui/settings-dialogs.js +7 -2
- package/dist/ui/settings-model.js +2 -1
- package/dist/ui/settings-strings.js +38 -2
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app-route.js +1 -1
- package/dist/ui/shared/app.css +29 -1
- package/dist/ui/shared/instance-nav.js +77 -8
- package/dist/ui/shared/signin.js +7 -7
- package/dist/ui/shared/tokens.css +9 -0
- package/dist/ui/shared/view-strings.js +2 -2
- package/dist/ui/signin.html +1 -1
- package/dist/update-install.d.ts +15 -1
- package/dist/update-install.js +6 -0
- package/dist/update-install.js.map +1 -1
- package/dist/update-preflight.d.ts +100 -0
- package/dist/update-preflight.js +264 -0
- package/dist/update-preflight.js.map +1 -0
- package/dist/updater-caller.d.ts +31 -0
- package/dist/updater-caller.js +109 -0
- package/dist/updater-caller.js.map +1 -0
- package/dist/view-api.d.ts +1 -1
- package/dist/view-api.js +33 -8
- package/dist/view-api.js.map +1 -1
- package/dist/wake-coordinator.d.ts +11 -0
- package/dist/wake-coordinator.js +38 -4
- package/dist/wake-coordinator.js.map +1 -1
- package/dist/web-api.d.ts +11 -0
- package/dist/web-api.js +54 -0
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.d.ts +2 -2
- package/dist/web-auth.js +5 -3
- package/dist/web-auth.js.map +1 -1
- package/dist/web-host-guard.d.ts +3 -11
- package/dist/web-host-guard.js +3 -12
- package/dist/web-host-guard.js.map +1 -1
- package/dist/web-images.d.ts +47 -0
- package/dist/web-images.js +169 -0
- package/dist/web-images.js.map +1 -0
- package/dist/web-session.d.ts +36 -1
- package/dist/web-session.js +52 -5
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +21 -0
- package/dist/web-settings-patch.js +74 -0
- package/dist/web-settings-patch.js.map +1 -0
- package/dist/web-shell-routes.d.ts +1 -1
- package/dist/web-shell-routes.js +1 -1
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/workflow-templates/default.md +1 -1
- package/package.json +1 -1
package/dist/daemon.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AGENT_WHATS_NEW, agendVersion, planWhatsNew, readWhatsNewRecord, recordWhatsNew, whatsNewNotice } from "./agent-whats-new.js";
|
|
1
2
|
import { join, dirname, basename, resolve } from "node:path";
|
|
2
3
|
import { mkdirSync, writeFileSync, readFileSync, readdirSync, existsSync, unlinkSync, rmSync, appendFileSync, statSync, renameSync } from "node:fs";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
@@ -77,6 +78,12 @@ const REPLY_GUARD_IDLE_CONFIRM_MS = 60_000;
|
|
|
77
78
|
* down like a turn the transcript cannot place — never a recovery.
|
|
78
79
|
*/
|
|
79
80
|
const TRANSCRIPT_RUNNING_HOLD_MAX_MS = 30 * 60_000;
|
|
81
|
+
/**
|
|
82
|
+
* #1641: an open turn in the CLI's transcript holds the instance busy (no paste, whatever the pane shows) only while the
|
|
83
|
+
* transcript is still moving: a turn whose end was missed must not pin the instance busy forever. A live turn writes
|
|
84
|
+
* often; a long silent tool run shows its own busy row on the pane.
|
|
85
|
+
*/
|
|
86
|
+
export const TRANSCRIPT_OPEN_TURN_QUIET_MS = 15 * 60_000;
|
|
80
87
|
const REPLY_RECOVERY_PROMPT = "[system:reply-required] The previous human-facing turn ended without a successfully delivered reply. Do not redo the work. React with an emoji or use the reply tool exactly once now to send the user a concise conclusion. If no substantive answer is needed, a brief react is sufficient. If you already replied to this message, do nothing. Do not reply to this system instruction except through the react or reply tool.";
|
|
81
88
|
/** Point a resumed CLI at its one backend-native instruction source. */
|
|
82
89
|
/** The outbox evidence for a delivery the CLI's transcript shows taken (#758; #1201 absorbed). */
|
|
@@ -1237,6 +1244,11 @@ export class Daemon extends EventEmitter {
|
|
|
1237
1244
|
turnReplyGuard = new TurnReplyGuard();
|
|
1238
1245
|
/** #1510: turn boundaries from the CLI's transcript, for a backend whose guard reads its turn end there. */
|
|
1239
1246
|
transcriptTurns = new TranscriptTurnLedger();
|
|
1247
|
+
/** #1641: when this launch's transcript monitor last read anything (a turn boundary, a tool, assistant text); performance.now(). */
|
|
1248
|
+
transcriptActivityAt = 0;
|
|
1249
|
+
/** #1641: the launch and composer a stuck pane's idle evidence is about, and since when (performance.now()); see recoverStuck. */
|
|
1250
|
+
stuckRecoveryKey = null;
|
|
1251
|
+
stuckRecoverySince = 0;
|
|
1240
1252
|
/** #1510: since when (monotonic) this generation has been held as running in the transcript. */
|
|
1241
1253
|
transcriptRunningSince = null;
|
|
1242
1254
|
/** Prevent a visible stale XML fragment from being recovered on later turns. */
|
|
@@ -1388,6 +1400,8 @@ export class Daemon extends EventEmitter {
|
|
|
1388
1400
|
recentPastes = [];
|
|
1389
1401
|
pendingInstructionsUpdate;
|
|
1390
1402
|
pendingInstructionsNotice = false;
|
|
1403
|
+
/** #1633: the one-time "what's new for you" notice, pasted before the next real delivery; recorded once pasted. */
|
|
1404
|
+
pendingWhatsNew = null;
|
|
1391
1405
|
// Whether the warmup steering-reload notice should be injected after spawn.
|
|
1392
1406
|
// Set in trySpawn by comparing the freshly-built instructions against the
|
|
1393
1407
|
// last value the agent was told about (prev-instructions). Skipped when
|
|
@@ -5041,9 +5055,68 @@ export class Daemon extends EventEmitter {
|
|
|
5041
5055
|
buildTranscriptMonitor(source) {
|
|
5042
5056
|
const monitor = new TranscriptMonitor(this.instanceDir, this.logger, source);
|
|
5043
5057
|
this.transcriptTurns.reset();
|
|
5044
|
-
|
|
5058
|
+
this.transcriptActivityAt = 0;
|
|
5059
|
+
// Monotonic: a wall-clock correction must neither end nor extend the open-turn hold (#1641 R1).
|
|
5060
|
+
const touch = () => { this.transcriptActivityAt = performance.now(); };
|
|
5061
|
+
monitor.on("turns", (turns) => { this.transcriptTurns.observe(turns, performance.now()); touch(); });
|
|
5062
|
+
// #1641 R2: a re-baselined source starts over, and so does what it said (an open turn read before is withdrawn).
|
|
5063
|
+
monitor.on("reset", () => { if (this.transcriptMonitor === monitor) {
|
|
5064
|
+
this.transcriptTurns.reset();
|
|
5065
|
+
this.transcriptActivityAt = 0;
|
|
5066
|
+
} });
|
|
5067
|
+
for (const event of ["tool_use", "tool_result", "assistant_text"])
|
|
5068
|
+
monitor.on(event, touch);
|
|
5045
5069
|
return monitor;
|
|
5046
5070
|
}
|
|
5071
|
+
/**
|
|
5072
|
+
* #1641: what the CLI's own transcript (codex's rollout) says about its turn. `open`: a turn started and has not ended,
|
|
5073
|
+
* and the transcript moved within TRANSCRIPT_OPEN_TURN_QUIET_MS. `closed`: every turn read since this launch's monitor
|
|
5074
|
+
* began has ended. `unknown`: not a transcript backend, nothing followed, or an open turn gone quiet (a missed end).
|
|
5075
|
+
*/
|
|
5076
|
+
transcriptTurnState(now = performance.now()) {
|
|
5077
|
+
if (this.backend?.turnEndFromTranscript !== true || this.transcriptMonitor?.following() !== true)
|
|
5078
|
+
return "unknown";
|
|
5079
|
+
// #1641 R2: an empty ledger after a baseline that could not see the current turn's start is not "closed".
|
|
5080
|
+
if (this.transcriptMonitor.turnStateKnown?.() !== true)
|
|
5081
|
+
return "unknown";
|
|
5082
|
+
const since = this.transcriptTurns.openSince();
|
|
5083
|
+
if (since === null)
|
|
5084
|
+
return "closed";
|
|
5085
|
+
return now - Math.max(since, this.transcriptActivityAt) < TRANSCRIPT_OPEN_TURN_QUIET_MS ? "open" : "unknown";
|
|
5086
|
+
}
|
|
5087
|
+
/**
|
|
5088
|
+
* #1641: a stuck pane that is in fact an idle CLI (a footer this build cannot read) must not stay stuck for hours with
|
|
5089
|
+
* every delivery parked behind it. It recovers when the backend sees its own live, EMPTY composer with no busy, queued
|
|
5090
|
+
* or dialog marker (isStableUnknownLayoutIdlePane) AND the transcript shows no open turn — or, with no transcript to
|
|
5091
|
+
* ask, that same composer held unchanged for UNKNOWN_LAYOUT_STABLE_MS (#978's rule). An open turn never recovers.
|
|
5092
|
+
*/
|
|
5093
|
+
recoverStuck(pane, snapshot, changeAt, turn) {
|
|
5094
|
+
const machine = this.instanceStateMachine;
|
|
5095
|
+
const idleComposer = this.backend?.isStableUnknownLayoutIdlePane?.(pane) === true;
|
|
5096
|
+
// The resume load (#1031: `Resuming session…` over the same composer) is input that is not open yet (#1641 R1).
|
|
5097
|
+
if (!machine || !idleComposer || turn === "open" || this.inputTransientInPane(pane)) {
|
|
5098
|
+
this.stuckRecoveryKey = null;
|
|
5099
|
+
return snapshot;
|
|
5100
|
+
}
|
|
5101
|
+
// The evidence is about this launch and this screen. Once it holds, it keeps holding — a still pane reads stuck
|
|
5102
|
+
// again at the next sweep (its footer is still unreadable) and must not start the window over (#1641 R1).
|
|
5103
|
+
const mono = performance.now();
|
|
5104
|
+
const key = `${this.spawnGeneration}:${this.launchAttempt}\n${pane.replace(/⋆/gu, "").replace(/[ \t]+$/gmu, "")}`;
|
|
5105
|
+
if (key !== this.stuckRecoveryKey) {
|
|
5106
|
+
this.stuckRecoveryKey = key;
|
|
5107
|
+
this.stuckRecoverySince = mono;
|
|
5108
|
+
}
|
|
5109
|
+
if (turn === "unknown" && mono - this.stuckRecoverySince < UNKNOWN_LAYOUT_STABLE_MS)
|
|
5110
|
+
return snapshot;
|
|
5111
|
+
// A footer this build cannot read stays unreadable: the same still pane reads stuck again at every sweep and is
|
|
5112
|
+
// recovered again. Only the edge into idle is news.
|
|
5113
|
+
const fields = { evidence: turn === "closed" ? "transcript_no_open_turn" : "stable_empty_composer", unchangedForMs: snapshot.unchangedForMs };
|
|
5114
|
+
if (this.instanceState !== "idle")
|
|
5115
|
+
this.logger.info(fields, "Stuck instance recovered — the CLI shows its idle composer and is not running a turn (#1641)");
|
|
5116
|
+
else
|
|
5117
|
+
this.logger.debug(fields, "Still idle on its unreadable footer — the stuck reading was recovered again (#1641)");
|
|
5118
|
+
return machine.observe(pane, Date.now(), { settled: true, changeAt, forceReady: true });
|
|
5119
|
+
}
|
|
5047
5120
|
replyCompletionGuardEnabled() {
|
|
5048
5121
|
return this.config.reply_completion_guard !== false
|
|
5049
5122
|
&& this.backend?.replyCompletionGuard === true;
|
|
@@ -5425,7 +5498,9 @@ export class Daemon extends EventEmitter {
|
|
|
5425
5498
|
// a pair every ~1.6s for as long as the repaint lasted (#1116). Real
|
|
5426
5499
|
// work cannot hide behind this: it changes the layout, the next
|
|
5427
5500
|
// output arms another probe, and the debounce capture still fires.
|
|
5428
|
-
|
|
5501
|
+
// #1641: never while the transcript shows a turn running — that idle-looking screen is mid-turn.
|
|
5502
|
+
if (this.instanceState === "idle" && this.backend?.isPeriodicRedrawIdlePane?.(pane) === true
|
|
5503
|
+
&& this.transcriptTurnState() !== "open")
|
|
5429
5504
|
return;
|
|
5430
5505
|
// A second burst overtook the probe. Do not bless its stale capture as
|
|
5431
5506
|
// idle: continuous output is the safer working signal, while the next
|
|
@@ -5458,6 +5533,10 @@ export class Daemon extends EventEmitter {
|
|
|
5458
5533
|
// decisions about *now*. The old double-observe expressed that by calling
|
|
5459
5534
|
// observe twice, which silently disabled the "content moved" branch.
|
|
5460
5535
|
const structuredPeriodicIdle = this.backend?.isPeriodicRedrawIdlePane;
|
|
5536
|
+
// #1641: the transcript outranks the pane: while it shows the turn running, no reading of this pane is idle (codex
|
|
5537
|
+
// hides its status row while streaming, leaving the idle composer and footer in view mid-turn).
|
|
5538
|
+
const transcriptTurn = this.transcriptTurnState();
|
|
5539
|
+
const transcriptBusy = transcriptTurn === "open";
|
|
5461
5540
|
let snapshot;
|
|
5462
5541
|
if (deliveryFallback !== null) {
|
|
5463
5542
|
// A missing footer cannot fall through to a broad ready match. All
|
|
@@ -5466,7 +5545,7 @@ export class Daemon extends EventEmitter {
|
|
|
5466
5545
|
settled: true,
|
|
5467
5546
|
changeAt: observedChangeAt,
|
|
5468
5547
|
forceReady: deliveryFallback,
|
|
5469
|
-
forceBusy: !deliveryFallback || paneActivity !== null,
|
|
5548
|
+
forceBusy: !deliveryFallback || paneActivity !== null || transcriptBusy,
|
|
5470
5549
|
});
|
|
5471
5550
|
}
|
|
5472
5551
|
else if (structuredPeriodicIdle && reason === "output_probe") {
|
|
@@ -5488,6 +5567,7 @@ export class Daemon extends EventEmitter {
|
|
|
5488
5567
|
snapshot = this.instanceStateMachine.observe(pane, Date.now(), {
|
|
5489
5568
|
settled: true,
|
|
5490
5569
|
changeAt: observedChangeAt,
|
|
5570
|
+
forceBusy: transcriptBusy,
|
|
5491
5571
|
});
|
|
5492
5572
|
}
|
|
5493
5573
|
}
|
|
@@ -5521,6 +5601,7 @@ export class Daemon extends EventEmitter {
|
|
|
5521
5601
|
settled: true,
|
|
5522
5602
|
changeAt: observedChangeAt,
|
|
5523
5603
|
forceReady: true,
|
|
5604
|
+
forceBusy: transcriptBusy,
|
|
5524
5605
|
});
|
|
5525
5606
|
}
|
|
5526
5607
|
else {
|
|
@@ -5569,6 +5650,7 @@ export class Daemon extends EventEmitter {
|
|
|
5569
5650
|
snapshot = this.instanceStateMachine.observe(pane, Date.now(), {
|
|
5570
5651
|
settled: true,
|
|
5571
5652
|
changeAt: observedChangeAt,
|
|
5653
|
+
forceBusy: transcriptBusy,
|
|
5572
5654
|
});
|
|
5573
5655
|
}
|
|
5574
5656
|
else {
|
|
@@ -5586,9 +5668,11 @@ export class Daemon extends EventEmitter {
|
|
|
5586
5668
|
snapshot = this.instanceStateMachine.observe(pane, Date.now(), {
|
|
5587
5669
|
settled,
|
|
5588
5670
|
changeAt: observedChangeAt,
|
|
5589
|
-
forceBusy: paneActivity !== null,
|
|
5671
|
+
forceBusy: paneActivity !== null || transcriptBusy,
|
|
5590
5672
|
});
|
|
5591
5673
|
}
|
|
5674
|
+
if (snapshot.state === "stuck")
|
|
5675
|
+
snapshot = this.recoverStuck(pane, snapshot, observedChangeAt, transcriptTurn);
|
|
5592
5676
|
this.applyInstanceStateSnapshot(snapshot, pane);
|
|
5593
5677
|
// Backends without a transcript feed can still say what they are doing, if
|
|
5594
5678
|
// their TUI names it. Free-riding on a capture that already happened: no
|
|
@@ -6011,12 +6095,14 @@ export class Daemon extends EventEmitter {
|
|
|
6011
6095
|
* here — a steered message must read EXACTLY like a queued one to the
|
|
6012
6096
|
* agent, or the two drift apart in what the agent is told about replying.
|
|
6013
6097
|
*/
|
|
6098
|
+
/** #1650: how this instance talks to the fleet — MCP tools, or agend-agent commands (agent_mode: cli). */
|
|
6099
|
+
agentMode() { return this.config.agent_mode === "cli" ? "cli" : "mcp"; }
|
|
6014
6100
|
formatInboundMessage(content, meta) {
|
|
6015
6101
|
const user = meta.user || "unknown";
|
|
6016
6102
|
const fromInstance = meta.from_instance;
|
|
6017
6103
|
let formatted;
|
|
6018
6104
|
if (fromInstance) {
|
|
6019
|
-
formatted = formatCrossInstanceInboundMessage(content, meta);
|
|
6105
|
+
formatted = formatCrossInstanceInboundMessage(content, meta, this.agentMode());
|
|
6020
6106
|
}
|
|
6021
6107
|
else {
|
|
6022
6108
|
const via = meta.source ? ` via ${meta.source}` : "";
|
|
@@ -6029,7 +6115,10 @@ export class Daemon extends EventEmitter {
|
|
|
6029
6115
|
formatted = `[Recent reactions: ${meta.pending_reactions}]\n${formatted}`;
|
|
6030
6116
|
}
|
|
6031
6117
|
formatted += renderHandoffMetadata(meta);
|
|
6032
|
-
|
|
6118
|
+
// #1650: a CLI-mode agent has no reply tool — it answers with the agend-agent command.
|
|
6119
|
+
formatted += this.agentMode() === "cli"
|
|
6120
|
+
? '\n(Reply with agend-agent reply "…" — do NOT respond with direct text)'
|
|
6121
|
+
: "\n(Reply using the reply tool — do NOT respond with direct text)";
|
|
6033
6122
|
}
|
|
6034
6123
|
if (meta.reply_to_text) {
|
|
6035
6124
|
formatted += `\n(reply_to: "${meta.reply_to_text}")`;
|
|
@@ -6291,6 +6380,23 @@ export class Daemon extends EventEmitter {
|
|
|
6291
6380
|
if (!this.isDeliveryEpochCurrent(deliveryEpoch))
|
|
6292
6381
|
return;
|
|
6293
6382
|
}
|
|
6383
|
+
const whatsNew = this.pendingWhatsNew ? this.currentWhatsNew(this.pendingWhatsNew) : null;
|
|
6384
|
+
if (whatsNew) {
|
|
6385
|
+
// #1633: here, inside the delivery lock and ahead of a real delivery — never into a busy turn, never to an
|
|
6386
|
+
// instance nobody writes to. Recorded only once pasted: a failed or cancelled paste stays pending.
|
|
6387
|
+
const pasted = await this.deliverMessage(whatsNew.notice, undefined, { deliveryEpoch });
|
|
6388
|
+
if (pasted && this.pendingWhatsNew === whatsNew) {
|
|
6389
|
+
this.pendingWhatsNew = null;
|
|
6390
|
+
try {
|
|
6391
|
+
recordWhatsNew(this.instanceDir, whatsNew.ids, whatsNew.version);
|
|
6392
|
+
}
|
|
6393
|
+
catch (err) {
|
|
6394
|
+
this.logger.warn({ err }, "what's-new record not written — the notice may repeat once");
|
|
6395
|
+
}
|
|
6396
|
+
}
|
|
6397
|
+
if (!this.isDeliveryEpochCurrent(deliveryEpoch))
|
|
6398
|
+
return;
|
|
6399
|
+
}
|
|
6294
6400
|
const status = channelStatus(meta);
|
|
6295
6401
|
// A fresh delivery begins a fresh turn — its bubble must not inherit
|
|
6296
6402
|
// the previous turn's tool list.
|
|
@@ -10426,6 +10532,49 @@ export class Daemon extends EventEmitter {
|
|
|
10426
10532
|
}
|
|
10427
10533
|
catch { /* non-fatal */ }
|
|
10428
10534
|
}
|
|
10535
|
+
/**
|
|
10536
|
+
* #1633 (#1639 review): the pending notice as the record stands NOW, just before the paste — it was decided at launch,
|
|
10537
|
+
* and the record may have changed while the delivery waited. Unreadable → nothing is sent (and nothing pending);
|
|
10538
|
+
* entries recorded meanwhile are dropped from it; none left → nothing.
|
|
10539
|
+
*/
|
|
10540
|
+
currentWhatsNew(pending) {
|
|
10541
|
+
const record = readWhatsNewRecord(this.instanceDir);
|
|
10542
|
+
if (record === "unreadable") {
|
|
10543
|
+
this.pendingWhatsNew = null;
|
|
10544
|
+
return null;
|
|
10545
|
+
}
|
|
10546
|
+
const known = new Set(record?.announced ?? []);
|
|
10547
|
+
const ids = pending.ids.filter(id => !known.has(id));
|
|
10548
|
+
if (!ids.length) {
|
|
10549
|
+
this.pendingWhatsNew = null;
|
|
10550
|
+
return null;
|
|
10551
|
+
}
|
|
10552
|
+
if (ids.length === pending.ids.length)
|
|
10553
|
+
return pending;
|
|
10554
|
+
const narrowed = { notice: whatsNewNotice(pending.version, AGENT_WHATS_NEW.filter(e => ids.includes(e.id))), ids, version: pending.version };
|
|
10555
|
+
this.pendingWhatsNew = narrowed;
|
|
10556
|
+
return narrowed;
|
|
10557
|
+
}
|
|
10558
|
+
/** #1633: what this instance has not been told about its AgEnD version yet (see agent-whats-new.ts). */
|
|
10559
|
+
prepareWhatsNew(fresh, version = agendVersion()) {
|
|
10560
|
+
try {
|
|
10561
|
+
const plan = planWhatsNew({ record: readWhatsNewRecord(this.instanceDir), fresh, version });
|
|
10562
|
+
if (!plan) {
|
|
10563
|
+
this.pendingWhatsNew = null;
|
|
10564
|
+
return;
|
|
10565
|
+
}
|
|
10566
|
+
if ("silent" in plan) {
|
|
10567
|
+
recordWhatsNew(this.instanceDir, plan.silent, plan.version);
|
|
10568
|
+
this.pendingWhatsNew = null;
|
|
10569
|
+
return;
|
|
10570
|
+
}
|
|
10571
|
+
this.pendingWhatsNew = plan;
|
|
10572
|
+
this.logger.info({ entries: plan.ids }, "what's-new notice pending for the next delivery");
|
|
10573
|
+
}
|
|
10574
|
+
catch (err) {
|
|
10575
|
+
this.logger.debug({ err }, "what's-new not prepared");
|
|
10576
|
+
}
|
|
10577
|
+
}
|
|
10429
10578
|
async trySpawnInsideGate(reuseWindow = false, startupTimeoutMs) {
|
|
10430
10579
|
this.startupAdmission?.();
|
|
10431
10580
|
const launchFence = this.launchFenceEpoch;
|
|
@@ -10435,6 +10584,8 @@ export class Daemon extends EventEmitter {
|
|
|
10435
10584
|
// instructionsReloadedOnResume) so the warmup steering-reload can be
|
|
10436
10585
|
// skipped when nothing changed.
|
|
10437
10586
|
this.lastBuiltInstructions = backendConfig.instructions ?? "";
|
|
10587
|
+
// #1633: decided before prev-instructions is first written — its absence is what says "never launched before".
|
|
10588
|
+
this.prepareWhatsNew(!existsSync(join(this.instanceDir, "prev-instructions")));
|
|
10438
10589
|
{
|
|
10439
10590
|
const prevFile = join(this.instanceDir, "prev-instructions");
|
|
10440
10591
|
let prev = "";
|