@songsid/agend 2.3.4-beta.4 → 2.3.4-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/dist/backend/claude-code.d.ts +3 -0
- package/dist/backend/claude-code.js +88 -3
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/grok.d.ts +0 -2
- package/dist/backend/grok.js +0 -22
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/types.d.ts +0 -2
- package/dist/backend/types.js.map +1 -1
- package/dist/daemon.d.ts +1 -1
- package/dist/daemon.js +62 -82
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.test.js +2 -2
- package/dist/delivery-outbox.test.js.map +1 -1
- package/dist/ui/shared/app.css +1 -15
- package/dist/ui/shared/panel-view.js +10 -185
- package/dist/ui/shared/view-strings.js +0 -23
- package/dist/view-api.d.ts +0 -15
- package/dist/view-api.js +33 -94
- package/dist/view-api.js.map +1 -1
- package/package.json +1 -1
package/dist/daemon.js
CHANGED
|
@@ -9622,29 +9622,28 @@ export class Daemon extends EventEmitter {
|
|
|
9622
9622
|
}
|
|
9623
9623
|
/** Write half only: callers must finish the availability wait before any final recovery ownership proof. */
|
|
9624
9624
|
async sendDeliveryEnterAfterAvailability(phase, stillCurrent, durableAttempt, onInputHeld) {
|
|
9625
|
-
// A recovery Enter may have waited for a transient while cancel or spawn
|
|
9626
|
-
//
|
|
9625
|
+
// A recovery Enter may have waited for a transient while cancel or spawn replaced the delivery: check here, and
|
|
9626
|
+
// again after every wait below, at the last point before the tmux write.
|
|
9627
|
+
// M-2290 (#2985 review, Fable): the mode wait can last up to 10 minutes, and a respawn or relaunch reuses the same
|
|
9628
|
+
// tmux object and window — so the spawn generation and launch fence are part of the fence too: an Enter meant for
|
|
9629
|
+
// the CLI this paste went to is never sent to its successor.
|
|
9627
9630
|
const tmux = this.tmux;
|
|
9628
|
-
const backend = this.backend;
|
|
9629
9631
|
const generation = this.spawnGeneration;
|
|
9630
9632
|
const fence = this.launchFenceEpoch;
|
|
9631
|
-
const
|
|
9632
|
-
const current = () => !!tmux && !!windowId && this.tmux === tmux && this.backend === backend
|
|
9633
|
-
&& this.spawnGeneration === generation && this.launchFenceEpoch === fence && tmux.getWindowId() === windowId
|
|
9633
|
+
const current = () => this.tmux === tmux && this.spawnGeneration === generation && this.launchFenceEpoch === fence
|
|
9634
9634
|
&& !this.deliveryWritesStopping && (!stillCurrent || stillCurrent());
|
|
9635
9635
|
if (!current())
|
|
9636
9636
|
return false;
|
|
9637
|
-
//
|
|
9638
|
-
// that a permission card has not replaced it. Read this exact pane again, without resolving any dialog.
|
|
9637
|
+
// The pasted text may still be in the box: the caller keeps it uncertain (no false failure, no second paste).
|
|
9639
9638
|
const hold = () => {
|
|
9640
|
-
onInputHeld?.();
|
|
9641
|
-
this.logger.warn({ phase }, "
|
|
9639
|
+
onInputHeld?.("pane-in-mode");
|
|
9640
|
+
this.logger.warn({ phase }, "The pane stayed in a tmux mode; Enter withheld");
|
|
9642
9641
|
return false;
|
|
9643
9642
|
};
|
|
9644
|
-
// M-2267 (#2971
|
|
9645
|
-
//
|
|
9646
|
-
//
|
|
9647
|
-
//
|
|
9643
|
+
// M-2267 (#2971): a pane in a tmux mode (a person left it in copy-mode) would take this Enter. Wait for the mode to
|
|
9644
|
+
// end, then look once more within the same deadline — a pane flickering back into a mode is waited out again, never
|
|
9645
|
+
// past the bound. (beta.5: #2958's composer proof that used to sit between the two looks is reverted, decision
|
|
9646
|
+
// f57474aa: it rejected Codex's footer after a resume and while busy. #2957 is open again.)
|
|
9648
9647
|
const modeDeadline = performance.now() + PANE_MODE_ENTER_WAIT_MS;
|
|
9649
9648
|
for (;;) {
|
|
9650
9649
|
const mode = await this.waitWhilePaneInMode(phase, current, modeDeadline);
|
|
@@ -9652,50 +9651,6 @@ export class Daemon extends EventEmitter {
|
|
|
9652
9651
|
return false;
|
|
9653
9652
|
if (mode === "held")
|
|
9654
9653
|
return hold();
|
|
9655
|
-
let pane;
|
|
9656
|
-
try {
|
|
9657
|
-
pane = await tmux.capturePane();
|
|
9658
|
-
}
|
|
9659
|
-
catch {
|
|
9660
|
-
return current() ? hold() : false;
|
|
9661
|
-
}
|
|
9662
|
-
if (!current())
|
|
9663
|
-
return false;
|
|
9664
|
-
let composer = false;
|
|
9665
|
-
try {
|
|
9666
|
-
if (this.runtimeDialogs().some(dialog => Daemon.dialogMatches(dialog, pane)))
|
|
9667
|
-
return hold();
|
|
9668
|
-
if (backend?.readInputRow)
|
|
9669
|
-
composer = backend.readInputRow(pane) != null;
|
|
9670
|
-
else if (backend?.inputDraft)
|
|
9671
|
-
composer = backend.inputDraft(pane) != null;
|
|
9672
|
-
else if (backend?.isDeliveryInputReadyPane)
|
|
9673
|
-
composer = this.deliveryInputReadyPane(pane);
|
|
9674
|
-
else if (backend?.readSteerComposer?.(pane) != null)
|
|
9675
|
-
composer = true;
|
|
9676
|
-
else if (backend) {
|
|
9677
|
-
// Legacy backends still need their own current ready chrome, never a menu's cursor or old transcript row.
|
|
9678
|
-
const shape = unrecognisedPromptShape(pane);
|
|
9679
|
-
const ownComposer = backend.isDeliveryComposerPane?.(pane) === true;
|
|
9680
|
-
if ((!selectionCard(pane, "CLI") || ownComposer) && (!shape || ownComposer || this.cliComposerShowing(pane, shape))) {
|
|
9681
|
-
const bottom = backend.getBottomReadyPattern?.();
|
|
9682
|
-
const ready = backend.getReadyPattern();
|
|
9683
|
-
ready.lastIndex = 0;
|
|
9684
|
-
composer = ownComposer || (bottom ? bottomRowIsReady(pane, bottom)
|
|
9685
|
-
: ready.test(pane.trimEnd().split("\n").slice(-COMPOSER_TAIL_ROWS).join("\n")));
|
|
9686
|
-
}
|
|
9687
|
-
}
|
|
9688
|
-
}
|
|
9689
|
-
catch {
|
|
9690
|
-
return hold();
|
|
9691
|
-
}
|
|
9692
|
-
if (!composer)
|
|
9693
|
-
return hold();
|
|
9694
|
-
if (!current())
|
|
9695
|
-
return false;
|
|
9696
|
-
// The proof stands only if the pane did not go into a mode while it was made: else wait, and prove again — within
|
|
9697
|
-
// the same deadline (#2971 re-review, Prism P2: a pane flickering in and out of a mode must not hold the write lock
|
|
9698
|
-
// past it), and never spinning: one poll interval before the next look.
|
|
9699
9654
|
if (await this.paneInTmuxMode(phase)) {
|
|
9700
9655
|
if (!current())
|
|
9701
9656
|
return false;
|
|
@@ -9710,6 +9665,37 @@ export class Daemon extends EventEmitter {
|
|
|
9710
9665
|
return false;
|
|
9711
9666
|
break;
|
|
9712
9667
|
}
|
|
9668
|
+
// M-2290 (the simple half of the #2957 redesign, leader xmsg-1793712561468): a dialog the backend RECOGNISES
|
|
9669
|
+
// (runtimeDialogs: a permission card, the dangerous-command card, …) that painted between the paste and this Enter
|
|
9670
|
+
// would take the key as its answer. Read the pane once more: such a dialog withholds the Enter, the caller keeps the
|
|
9671
|
+
// delivery uncertain (Needs-you), and the dialog scanner parks the dialog for a person. Nothing else is judged here
|
|
9672
|
+
// — an unknown or unreadable screen sends, exactly as before: #2958's composer proof (decision f57474aa) is NOT back.
|
|
9673
|
+
// Only a caller that can hold (onInputHeld: the delivery's initial submit, settled uncertain → Needs-you) asks: one
|
|
9674
|
+
// that cannot would read `false` as a failure and paste again. Only the backend's OWN dialogs count — not AgEnD's
|
|
9675
|
+
// unrecognised-prompt guess — so an unknown screen still sends.
|
|
9676
|
+
if (onInputHeld) {
|
|
9677
|
+
let finalPane = null;
|
|
9678
|
+
try {
|
|
9679
|
+
finalPane = await tmux.capturePane();
|
|
9680
|
+
}
|
|
9681
|
+
catch {
|
|
9682
|
+
finalPane = null;
|
|
9683
|
+
}
|
|
9684
|
+
if (!current())
|
|
9685
|
+
return false;
|
|
9686
|
+
let dialog;
|
|
9687
|
+
try {
|
|
9688
|
+
dialog = finalPane === null ? undefined : (this.backend?.getRuntimeDialogs?.() ?? []).find(d => Daemon.dialogMatches(d, finalPane))?.description;
|
|
9689
|
+
}
|
|
9690
|
+
catch {
|
|
9691
|
+
dialog = undefined;
|
|
9692
|
+
}
|
|
9693
|
+
if (dialog !== undefined) {
|
|
9694
|
+
onInputHeld?.("dialog");
|
|
9695
|
+
this.logger.warn({ phase, dialog }, "A dialog is on the pane; the delivery's Enter is withheld (it would answer it)");
|
|
9696
|
+
return false;
|
|
9697
|
+
}
|
|
9698
|
+
}
|
|
9713
9699
|
// W1: persist the "Enter may have happened" boundary before tmux receives
|
|
9714
9700
|
// the key. A crash after this commit is never auto-replayed without a
|
|
9715
9701
|
// positive transcript marker, even if the stale pane still shows composer.
|
|
@@ -9981,22 +9967,23 @@ export class Daemon extends EventEmitter {
|
|
|
9981
9967
|
let enterAt = Date.now();
|
|
9982
9968
|
const enterMono = performance.now();
|
|
9983
9969
|
const steerEnterCurrent = () => steerCurrent() && performance.now() <= steerEnterDeadline;
|
|
9984
|
-
let inputHeld =
|
|
9985
|
-
if (!(await this.sendDeliveryEnter("initial-submit", steerEnterCurrent, durableAttempt, () => { inputHeld =
|
|
9970
|
+
let inputHeld = null;
|
|
9971
|
+
if (!(await this.sendDeliveryEnter("initial-submit", steerComposerProof ? steerEnterCurrent : undefined, durableAttempt, (why) => { inputHeld = why; }))) {
|
|
9986
9972
|
if (inputHeld) {
|
|
9987
|
-
//
|
|
9973
|
+
// #2971: held in a tmux mode past the bound; M-2290: a recognised dialog on the pane. The paste may still be in
|
|
9974
|
+
// the CLI: keep it uncertain (Needs-you), never re-paste, never answer the dialog.
|
|
9988
9975
|
verdict.phase = "submit-enter";
|
|
9989
|
-
verdict.proof = "
|
|
9976
|
+
verdict.proof = inputHeld === "dialog" ? "dialog-on-pane" : "pane-in-mode-held";
|
|
9990
9977
|
return false;
|
|
9991
9978
|
}
|
|
9992
|
-
if (!steerCurrent()) {
|
|
9979
|
+
if (steerComposerProof && !steerCurrent()) {
|
|
9993
9980
|
verdict.phase = "submit-enter";
|
|
9994
9981
|
verdict.proof = "fenced-before-enter";
|
|
9995
9982
|
return false;
|
|
9996
9983
|
}
|
|
9997
9984
|
if (steerComposerProof && performance.now() > steerEnterDeadline)
|
|
9998
9985
|
return this.failDelivery(verdict, status, "submit-enter", "pre-enter-deadline");
|
|
9999
|
-
return this.failDelivery(verdict, status, "submit-enter", "
|
|
9986
|
+
return this.failDelivery(verdict, status, "submit-enter", "tmux-send-keys-failed");
|
|
10000
9987
|
}
|
|
10001
9988
|
// The proof's whole budget, from the Enter: every capture and dialog probe inside it, a late answer dropped.
|
|
10002
9989
|
const steerProofDeadline = performance.now() + NATIVE_QUEUE_PASTE_VERIFY_MS + POST_ENTER_PROOF_WINDOW_MS;
|
|
@@ -10018,7 +10005,7 @@ export class Daemon extends EventEmitter {
|
|
|
10018
10005
|
if (!rawPaste && !steerComposerProof && this.backend?.requiresDeliveryEnterRetry?.() === true) {
|
|
10019
10006
|
await new Promise(r => setTimeout(r, 1_000));
|
|
10020
10007
|
const retryAt = Date.now();
|
|
10021
|
-
if (await this.sendDeliveryEnter("queue-less-defensive-retry"
|
|
10008
|
+
if (await this.sendDeliveryEnter("queue-less-defensive-retry")) {
|
|
10022
10009
|
enterAt = retryAt;
|
|
10023
10010
|
enterRetry = true;
|
|
10024
10011
|
this.logger.debug("Sent defensive Enter retry (queue-less TUI can swallow the first)");
|
|
@@ -10149,7 +10136,7 @@ export class Daemon extends EventEmitter {
|
|
|
10149
10136
|
// again. The prompt is back now, so an Enter can land.
|
|
10150
10137
|
this.logger.warn("Message still in the input row after idle — submitting the existing text instead of pasting it again");
|
|
10151
10138
|
const strandedAt = Date.now();
|
|
10152
|
-
if (!(await this.sendDeliveryEnter("native-queue-stranded-submit"
|
|
10139
|
+
if (!(await this.sendDeliveryEnter("native-queue-stranded-submit"))) {
|
|
10153
10140
|
return this.failDelivery(verdict, status, "native-queue-submit", "tmux-send-keys-failed");
|
|
10154
10141
|
}
|
|
10155
10142
|
// The recovery Enter can be accepted before Codex paints its new
|
|
@@ -10202,7 +10189,7 @@ export class Daemon extends EventEmitter {
|
|
|
10202
10189
|
}
|
|
10203
10190
|
await new Promise(r => setTimeout(r, NORMAL_ENTER_SETTLE_MS));
|
|
10204
10191
|
const retryAt = Date.now();
|
|
10205
|
-
if (!(await this.sendDeliveryEnter("native-queue-idle-redelivery"
|
|
10192
|
+
if (!(await this.sendDeliveryEnter("native-queue-idle-redelivery"))) {
|
|
10206
10193
|
return this.failDelivery(verdict, status, "native-queue-redelivery", "tmux-send-keys-failed");
|
|
10207
10194
|
}
|
|
10208
10195
|
if (windowId && this.controlClient) {
|
|
@@ -10254,7 +10241,7 @@ export class Daemon extends EventEmitter {
|
|
|
10254
10241
|
this.logger.warn("Message not submitted after Enter — waiting for the prompt, then re-sending Enter once");
|
|
10255
10242
|
const promptBack = await this.waitForPaneReadyForDelivery(windowId, STRANDED_RETRY_READY_WAIT_MS);
|
|
10256
10243
|
const retryAt = Date.now();
|
|
10257
|
-
if (promptBack && !(await this.sendDeliveryEnter("stranded-text-retry"
|
|
10244
|
+
if (promptBack && !(await this.sendDeliveryEnter("stranded-text-retry"))) {
|
|
10258
10245
|
return this.failDelivery(verdict, status, "stranded-text-retry", "tmux-send-keys-failed");
|
|
10259
10246
|
}
|
|
10260
10247
|
submitted = promptBack && await this.confirmSubmittedAfterEnter(windowId, retryAt, formatted);
|
|
@@ -10342,7 +10329,7 @@ export class Daemon extends EventEmitter {
|
|
|
10342
10329
|
}
|
|
10343
10330
|
// No control client to observe output: fall back to the legacy double-Enter.
|
|
10344
10331
|
await new Promise(r => setTimeout(r, 1000));
|
|
10345
|
-
await this.sendDeliveryEnter("unobserved-defensive-retry"
|
|
10332
|
+
await this.sendDeliveryEnter("unobserved-defensive-retry");
|
|
10346
10333
|
if (status)
|
|
10347
10334
|
this.emit("message_confirmed", status); // ✅ (best-effort)
|
|
10348
10335
|
}
|
|
@@ -10810,27 +10797,20 @@ export class Daemon extends EventEmitter {
|
|
|
10810
10797
|
* `guard` is for a paste that must not outlive the reason it was made (the continue nudge): `accept` judges the ONE
|
|
10811
10798
|
* capture taken here — the same picture is the baseline, the expected-screen check and the idle check — and `current`
|
|
10812
10799
|
* is asked again after every await that precedes a write, so a cancelled paste adds no further paste and no key
|
|
10813
|
-
* (the retries included).
|
|
10800
|
+
* (the retries included). Without a guard it is exactly the old unconditional path.
|
|
10814
10801
|
*/
|
|
10815
10802
|
async submitSystemPaste(text, label, guard, admission) {
|
|
10816
|
-
const
|
|
10817
|
-
|
|
10818
|
-
const current = () => {
|
|
10819
|
-
admission?.();
|
|
10820
|
-
return !!tmux && !!windowId && this.tmux === tmux && tmux.getWindowId() === windowId
|
|
10821
|
-
&& this.spawnGeneration === generation && this.launchFenceEpoch === fence
|
|
10822
|
-
&& !this.deliveryWritesStopping && (guard?.current() ?? true);
|
|
10823
|
-
};
|
|
10824
|
-
if (!current())
|
|
10803
|
+
const current = guard || admission ? () => { admission?.(); return guard?.current() ?? true; } : undefined;
|
|
10804
|
+
if (current && !current())
|
|
10825
10805
|
return false;
|
|
10826
|
-
if (!tmux)
|
|
10806
|
+
if (!this.tmux)
|
|
10827
10807
|
return false;
|
|
10828
10808
|
const signature = this.submissionSignature(text);
|
|
10829
10809
|
let baseline;
|
|
10830
10810
|
if (guard) {
|
|
10831
10811
|
let pane;
|
|
10832
10812
|
try {
|
|
10833
|
-
pane = await tmux.capturePane();
|
|
10813
|
+
pane = await this.tmux.capturePane();
|
|
10834
10814
|
}
|
|
10835
10815
|
catch {
|
|
10836
10816
|
return false;
|
|
@@ -10845,7 +10825,7 @@ export class Daemon extends EventEmitter {
|
|
|
10845
10825
|
if (current && !current())
|
|
10846
10826
|
return false;
|
|
10847
10827
|
const pasteGeneration = this.spawnGeneration;
|
|
10848
|
-
const pasted = current ? await tmux.pasteBuffer(text, { guard: current }) : await tmux.pasteBuffer(text);
|
|
10828
|
+
const pasted = current ? await this.tmux.pasteBuffer(text, { guard: current }) : await this.tmux.pasteBuffer(text);
|
|
10849
10829
|
if (!pasted) {
|
|
10850
10830
|
this.logger.warn({ label }, "System paste failed to reach the pane");
|
|
10851
10831
|
return false;
|