@songsid/agend 2.2.0 → 2.2.1

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/daemon.d.ts CHANGED
@@ -772,6 +772,8 @@ export declare class Daemon extends EventEmitter {
772
772
  private pendingInstructionsNotice;
773
773
  /** #1633: the one-time "what's new for you" notice, pasted before the next real delivery; recorded once pasted. */
774
774
  private pendingWhatsNew;
775
+ /** #2140: deliveries tried for the pending what's-new notice that never reached the pane (capped: never before every message). */
776
+ private whatsNewUnwritten;
775
777
  private warmupNeeded;
776
778
  private lastBuiltInstructions;
777
779
  private pasteQueueDepth;
package/dist/daemon.js CHANGED
@@ -52,6 +52,8 @@ import { codexLiveRowMatches } from "./backend/codex.js";
52
52
  import { claudeConfigDirFor, claudeProfileConfigDir, claudeSwitchDialog, claudeSwitchTargetMatches } from "./backend/claude-code.js";
53
53
  import { InteractionObservation, INTERACTION_CONFIRM_MS, sameInteractionOwner } from "./interaction-observation.js";
54
54
  import { unrecognisedPromptShape } from "./unrecognised-prompt.js";
55
+ /** #2140: deliveries that may carry an unwritten what's-new notice before it is recorded anyway. */
56
+ const WHATS_NEW_MAX_UNWRITTEN = 3;
55
57
  const __filename = fileURLToPath(import.meta.url);
56
58
  const __dirname = dirname(__filename);
57
59
  // Tool routing sets — module-level to avoid re-creation on every handleToolCall
@@ -1466,6 +1468,8 @@ export class Daemon extends EventEmitter {
1466
1468
  pendingInstructionsNotice = false;
1467
1469
  /** #1633: the one-time "what's new for you" notice, pasted before the next real delivery; recorded once pasted. */
1468
1470
  pendingWhatsNew = null;
1471
+ /** #2140: deliveries tried for the pending what's-new notice that never reached the pane (capped: never before every message). */
1472
+ whatsNewUnwritten = 0;
1469
1473
  // Whether the warmup steering-reload notice should be injected after spawn.
1470
1474
  // Set in trySpawn by comparing the freshly-built instructions against the
1471
1475
  // last value the agent was told about (prev-instructions). Skipped when
@@ -6770,15 +6774,30 @@ export class Daemon extends EventEmitter {
6770
6774
  const whatsNew = this.pendingWhatsNew ? this.currentWhatsNew(this.pendingWhatsNew) : null;
6771
6775
  if (whatsNew) {
6772
6776
  // #1633: here, inside the delivery lock and ahead of a real delivery — never into a busy turn, never to an
6773
- // instance nobody writes to. Recorded only once pasted: a failed or cancelled paste stays pending.
6774
- const pasted = await this.deliverMessage(whatsNew.notice, undefined, { deliveryEpoch });
6775
- if (pasted && this.pendingWhatsNew === whatsNew) {
6776
- this.pendingWhatsNew = null;
6777
- try {
6778
- recordWhatsNew(this.instanceDir, whatsNew.ids, whatsNew.version);
6779
- }
6780
- catch (err) {
6781
- this.logger.warn({ err }, "what's-new record not written — the notice may repeat once");
6777
+ // instance nobody writes to. #2140: told at most once. Once its paste reached the pane it is recorded, proven
6778
+ // submitted or not. A system paste has no unique id: its only pane proof is its opening line on the screen, and
6779
+ // a long notice (every entry since an old session began) scrolls that line above the captured rows once Claude
6780
+ // paints it — unproven, though the agent read it. One that never reached the pane (cancelled, never idle) is
6781
+ // tried again, at most WHATS_NEW_MAX_UNWRITTEN times, then recorded with a warning.
6782
+ const attempt = { reached: false };
6783
+ const pasted = await this.deliverMessage(whatsNew.notice, undefined, { deliveryEpoch, verdict: attempt });
6784
+ if (this.pendingWhatsNew === whatsNew) {
6785
+ const written = pasted || attempt.paneWriteStarted === true;
6786
+ if (!written)
6787
+ this.whatsNewUnwritten++;
6788
+ if (written || this.whatsNewUnwritten >= WHATS_NEW_MAX_UNWRITTEN) {
6789
+ if (!pasted) {
6790
+ this.logger.warn({ entries: whatsNew.ids, written, attempts: this.whatsNewUnwritten, phase: attempt.phase, proof: attempt.proof }, written ? "what's-new notice pasted but its submission could not be proven — recorded so it is not sent again"
6791
+ : "what's-new notice did not reach the pane after several deliveries — recorded so it is not sent again");
6792
+ }
6793
+ this.pendingWhatsNew = null;
6794
+ this.whatsNewUnwritten = 0;
6795
+ try {
6796
+ recordWhatsNew(this.instanceDir, whatsNew.ids, whatsNew.version);
6797
+ }
6798
+ catch (err) {
6799
+ this.logger.warn({ err }, "what's-new record not written — the notice may repeat once");
6800
+ }
6782
6801
  }
6783
6802
  }
6784
6803
  if (!this.isDeliveryEpochCurrent(deliveryEpoch))
@@ -11216,6 +11235,7 @@ export class Daemon extends EventEmitter {
11216
11235
  return;
11217
11236
  }
11218
11237
  this.pendingWhatsNew = plan;
11238
+ this.whatsNewUnwritten = 0;
11219
11239
  this.logger.info({ entries: plan.ids }, "what's-new notice pending for the next delivery");
11220
11240
  }
11221
11241
  catch (err) {