@songsid/agend 2.1.12-beta.6 → 2.1.12-beta.8

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.
Files changed (61) hide show
  1. package/dist/agent-cli.js +13 -2
  2. package/dist/agent-cli.js.map +1 -1
  3. package/dist/backend/claude-code.d.ts +34 -1
  4. package/dist/backend/claude-code.js +123 -0
  5. package/dist/backend/claude-code.js.map +1 -1
  6. package/dist/backend/types.d.ts +21 -0
  7. package/dist/backend/types.js.map +1 -1
  8. package/dist/channel/adapters/discord.d.ts +13 -0
  9. package/dist/channel/adapters/discord.js +43 -0
  10. package/dist/channel/adapters/discord.js.map +1 -1
  11. package/dist/channel/mcp-tools.js +1 -1
  12. package/dist/channel/mcp-tools.js.map +1 -1
  13. package/dist/cli.js +26 -0
  14. package/dist/cli.js.map +1 -1
  15. package/dist/command-table.d.ts +1 -1
  16. package/dist/command-table.js +7 -1
  17. package/dist/command-table.js.map +1 -1
  18. package/dist/config-validator.js +4 -0
  19. package/dist/config-validator.js.map +1 -1
  20. package/dist/cpu-profile.d.ts +32 -0
  21. package/dist/cpu-profile.js +221 -0
  22. package/dist/cpu-profile.js.map +1 -0
  23. package/dist/daemon.d.ts +27 -1
  24. package/dist/daemon.js +131 -30
  25. package/dist/daemon.js.map +1 -1
  26. package/dist/delivery-outbox.d.ts +22 -0
  27. package/dist/delivery-outbox.js +111 -0
  28. package/dist/delivery-outbox.js.map +1 -1
  29. package/dist/fleet-context.d.ts +10 -1
  30. package/dist/fleet-manager.d.ts +66 -2
  31. package/dist/fleet-manager.js +409 -62
  32. package/dist/fleet-manager.js.map +1 -1
  33. package/dist/locale.js +26 -0
  34. package/dist/locale.js.map +1 -1
  35. package/dist/outbound-handlers.d.ts +2 -0
  36. package/dist/outbound-handlers.js +8 -0
  37. package/dist/outbound-handlers.js.map +1 -1
  38. package/dist/outbound-schemas.d.ts +15 -1
  39. package/dist/outbound-schemas.js +9 -3
  40. package/dist/outbound-schemas.js.map +1 -1
  41. package/dist/pane-input-residue.d.ts +2 -0
  42. package/dist/pane-input-residue.js +5 -1
  43. package/dist/pane-input-residue.js.map +1 -1
  44. package/dist/profile-control.d.ts +20 -0
  45. package/dist/profile-control.js +308 -0
  46. package/dist/profile-control.js.map +1 -0
  47. package/dist/runtime-cpu-profile.d.ts +37 -0
  48. package/dist/runtime-cpu-profile.js +123 -0
  49. package/dist/runtime-cpu-profile.js.map +1 -0
  50. package/dist/scheduler/db.d.ts +29 -4
  51. package/dist/scheduler/db.js +96 -6
  52. package/dist/scheduler/db.js.map +1 -1
  53. package/dist/scheduler/types.d.ts +33 -0
  54. package/dist/service-installer.d.ts +6 -0
  55. package/dist/service-installer.js +18 -2
  56. package/dist/service-installer.js.map +1 -1
  57. package/dist/topic-commands.d.ts +12 -1
  58. package/dist/topic-commands.js +33 -1
  59. package/dist/topic-commands.js.map +1 -1
  60. package/dist/types.d.ts +4 -0
  61. package/package.json +1 -1
package/dist/daemon.js CHANGED
@@ -30,7 +30,7 @@ import { writeSecretFile } from "./secret-file.js";
30
30
  import { PaneWriteLock } from "./pane-write-lock.js";
31
31
  import { buildFleetInstructions } from "./instructions.js";
32
32
  import { formatCrossInstanceInboundMessage, renderCrossInstanceHandoffMetadata, } from "./cross-instance-envelope.js";
33
- import { bottomRowIsReady, inputAreaText, inputShowsPastedText, pastedTextSignature, pasteLeftInInput, strandedAgendMessageInInput } from "./pane-input-residue.js";
33
+ import { agendMessageInInput, bottomRowIsReady, inputAreaText, inputShowsPastedText, pastedTextSignature, pasteLeftInInput, strandedAgendMessageInInput } from "./pane-input-residue.js";
34
34
  import { TurnReplyGuard } from "./turn-reply-guard.js";
35
35
  import { t } from "./locale.js";
36
36
  import { MuseUsageRelay, clearMuseUsageSnapshot } from "./muse-usage-relay.js";
@@ -6143,7 +6143,7 @@ export class Daemon extends EventEmitter {
6143
6143
  const canHandOff = (supportsQueuedInput || opts?.steer)
6144
6144
  && readiness === "busy"
6145
6145
  && (await this.probeBlockingDialog()).state === "clear"
6146
- && await this.hasPositiveDeliveryInput();
6146
+ && await this.hasPositiveDeliveryInput(true);
6147
6147
  if (canHandOff) {
6148
6148
  // Native queue (codex), or an explicit /steer: hand the complete
6149
6149
  // paste+Enter transaction to the busy CLI now. For steer this is the
@@ -6240,7 +6240,7 @@ export class Daemon extends EventEmitter {
6240
6240
  if (probe.state !== "clear")
6241
6241
  return "dialog";
6242
6242
  }
6243
- if (!(await this.hasPositiveDeliveryInput()))
6243
+ if (!(await this.hasPositiveDeliveryInput(handingOffToNativeQueue)))
6244
6244
  return "dialog";
6245
6245
  // #829: a CLI that restores a cancelled prompt into its input box would
6246
6246
  // have this message pasted onto it and both submitted as one. Clear it
@@ -6262,6 +6262,9 @@ export class Daemon extends EventEmitter {
6262
6262
  if (residue !== "clear")
6263
6263
  return this.failDelivery(verdict, status, "input-residue", residue);
6264
6264
  const written = await this.writeMessageToPane(formatted, windowId, handingOffToNativeQueue, status, opts?.submissionId, verdict, opts?.durableAttempt, opts?.steer === true, writeCurrent);
6265
+ // The hand-off's last capture had no box: nothing was begun or written — wait for readiness, then try again.
6266
+ if (written === "handoff-box-unread")
6267
+ return "dialog";
6265
6268
  // Fenced before its first write: not attempted — redo after a spawn, else drop without a ❌.
6266
6269
  return written === false && verdict.fenced ? stale() : written;
6267
6270
  });
@@ -6809,8 +6812,26 @@ export class Daemon extends EventEmitter {
6809
6812
  }
6810
6813
  return true;
6811
6814
  }
6812
- /** Codex can paint a prompt before the TTY enters raw mode. Both are required. */
6813
- async hasPositiveDeliveryInput() {
6815
+ /**
6816
+ * Codex can paint a prompt before the TTY enters raw mode. Both are required.
6817
+ *
6818
+ * A hand-off into a busy pane (native queue or steer) pastes and presses Enter at once, so a backend with a structural
6819
+ * box reader must see its box on a fresh capture first: a pane whose box cannot be read — a modal the dialog table
6820
+ * does not know, a frame the reader refuses, a failed capture — is not one to type into (#1169 review). Asked
6821
+ * before the hand-off is chosen and again under the pane lock, right before the write; false waits for readiness.
6822
+ */
6823
+ async hasPositiveDeliveryInput(handOff = false) {
6824
+ if (handOff && this.backend?.readInputRow) {
6825
+ if (!this.tmux)
6826
+ return false;
6827
+ try {
6828
+ if (this.backend.readInputRow(await this.tmux.capturePane()) === null)
6829
+ return false;
6830
+ }
6831
+ catch {
6832
+ return false;
6833
+ }
6834
+ }
6814
6835
  if (!this.needsStartupInputProof())
6815
6836
  return true;
6816
6837
  const check = this.backend?.isDeliveryInputReadyPane;
@@ -7494,6 +7515,15 @@ export class Daemon extends EventEmitter {
7494
7515
  verdict.fenced = !verdict.durableBeginCommitted;
7495
7516
  return false;
7496
7517
  }
7518
+ // A hand-off pastes and presses Enter into a busy pane: the box must still be readable on this, the last capture
7519
+ // before the write (#1169 review) — a modal that appeared after the under-lock check would take that Enter. Before
7520
+ // the durable begin the caller waits for readiness outside the lock and tries again; after it (a paste retry),
7521
+ // nothing was written and the attempt ends like a paste that failed.
7522
+ if (handingOffToNativeQueue && this.backend?.readInputRow && !pasteBaseline?.inputReadable) {
7523
+ if (!verdict.durableBeginCommitted)
7524
+ return "handoff-box-unread";
7525
+ return this.failDelivery(verdict, status, "paste", "handoff-input-unreadable");
7526
+ }
7497
7527
  // Readiness, idle-gate, dialog, spawn and pane-lock waits have all ended.
7498
7528
  // Commit the submission fence at the last possible point before the
7499
7529
  // first side effect; a crash during those waits remains safely replayable.
@@ -7700,7 +7730,7 @@ export class Daemon extends EventEmitter {
7700
7730
  // The recovery Enter can be accepted before Codex paints its new
7701
7731
  // transcript. One immediate capture is not a failure verdict: give
7702
7732
  // that echo/queue a bounded chance to appear, without re-pasting.
7703
- const afterEnter = this.backend?.isDeliveryInputReadyPane
7733
+ const afterEnter = this.structuredInputEvidence()
7704
7734
  ? await this.lateCodexSubmissionProof(signature, pasteBaseline, true)
7705
7735
  : await this.confirmSubmitted(signature, pasteBaseline);
7706
7736
  if (afterEnter === "submitted") {
@@ -7714,7 +7744,7 @@ export class Daemon extends EventEmitter {
7714
7744
  // another turn's output — and treating that as proof re-confirms a
7715
7745
  // message nobody submitted. Output is corroboration; text sitting in
7716
7746
  // the input row is disqualifying, and disqualifying evidence wins.
7717
- if (this.backend?.isDeliveryInputReadyPane && afterEnter !== "stranded") {
7747
+ if (this.structuredInputEvidence() && afterEnter !== "stranded") {
7718
7748
  this.logger.warn({ phase: "native-queue-submit", proof: afterEnter, strandedAt }, "Codex recovery Enter outcome uncertain — no hard failure or duplicate paste");
7719
7749
  verdict.phase = "native-queue-submit";
7720
7750
  verdict.proof = afterEnter;
@@ -7723,7 +7753,11 @@ export class Daemon extends EventEmitter {
7723
7753
  this.logger.error({ afterEnter, strandedAt }, "Stranded message could not be submitted by Enter");
7724
7754
  return this.failDelivery(verdict, status, "native-queue-submit", afterEnter);
7725
7755
  }
7726
- if (this.backend?.isDeliveryInputReadyPane) {
7756
+ // A structural box reader (claude-code, #1200) is in the same position as Codex once the turn has run: the
7757
+ // message may have been taken and its echo scrolled out, the queue drained, the box empty — or the capture
7758
+ // failed. None of that proves the paste was lost, so nothing short of positive evidence is a reason to paste
7759
+ // again (#1353 review); "unverifiable" used to, and could submit the message twice.
7760
+ if (this.backend?.isDeliveryInputReadyPane || this.backend?.readInputRow) {
7727
7761
  // In Codex a missing viewport echo is inconclusive, not a proof of
7728
7762
  // loss. Another paste could run the same request twice. Leave the
7729
7763
  // already-pasted delivery at 👀 and let the next observation decide.
@@ -7824,7 +7858,7 @@ export class Daemon extends EventEmitter {
7824
7858
  verdict.proof = "output-edge";
7825
7859
  }
7826
7860
  else {
7827
- const proof = this.backend?.isDeliveryInputReadyPane
7861
+ const proof = this.structuredInputEvidence()
7828
7862
  ? await this.lateCodexSubmissionProof(signature, pasteBaseline)
7829
7863
  : await this.confirmSubmitted(signature, pasteBaseline);
7830
7864
  if (proof === "submitted") {
@@ -7832,7 +7866,7 @@ export class Daemon extends EventEmitter {
7832
7866
  this.emit("message_confirmed", status);
7833
7867
  return true;
7834
7868
  }
7835
- if (this.backend?.isDeliveryInputReadyPane && proof !== "stranded") {
7869
+ if (this.structuredInputEvidence() && proof !== "stranded") {
7836
7870
  // This is the observed #910 race: the CLI processed the message
7837
7871
  // although its echo had not appeared within the proof window. A
7838
7872
  // missing viewport signature cannot establish non-delivery; keep
@@ -7910,7 +7944,7 @@ export class Daemon extends EventEmitter {
7910
7944
  return dialogClear && this.spawnGeneration === spawnGeneration && this.getWindowId() === windowId;
7911
7945
  }
7912
7946
  canProveSubmission() {
7913
- return !!this.backend?.getBottomReadyPattern?.();
7947
+ return this.readsInput();
7914
7948
  }
7915
7949
  /** Positive submission retires the startup transient guard for this spawn. */
7916
7950
  submittedProof() {
@@ -7947,7 +7981,7 @@ export class Daemon extends EventEmitter {
7947
7981
  }
7948
7982
  return busy;
7949
7983
  }
7950
- if (this.backend?.isDeliveryInputReadyPane) {
7984
+ if (this.structuredInputEvidence()) {
7951
7985
  // Codex's first post-wake redraw may hide the echo for a few seconds.
7952
7986
  // Absence from a viewport is NOT proof the paste was lost, so only a
7953
7987
  // positively identified strand authorizes another Enter. Never re-paste
@@ -8015,6 +8049,14 @@ export class Daemon extends EventEmitter {
8015
8049
  catch {
8016
8050
  return "unproven";
8017
8051
  }
8052
+ return this.judgeSubmission(pane, signature, baseline);
8053
+ }
8054
+ /**
8055
+ * The verdict one snapshot supports — the live viewport, or (for a unique signature) the scrollback a late proof
8056
+ * reads. One judge for both, so a history read cannot vouch for what the live one refuses: an unreadable box, or our
8057
+ * text still in it (#1353 review).
8058
+ */
8059
+ judgeSubmission(pane, signature, baseline) {
8018
8060
  // Without a way to tell the input row from the transcript, "the text is on
8019
8061
  // screen" cannot distinguish submitted from stranded — that ambiguity IS
8020
8062
  // the bug. Such a backend gets an explicit verdict rather than a guess:
@@ -8024,8 +8066,7 @@ export class Daemon extends EventEmitter {
8024
8066
  // for the first and recovers on the second, which is what it did before —
8025
8067
  // the fix here is for backends that CAN be read, not a new guess for those
8026
8068
  // that cannot.
8027
- const prompt = this.backend?.getBottomReadyPattern?.();
8028
- if (!prompt) {
8069
+ if (!this.readsInput()) {
8029
8070
  const seen = this.paneEvidence(pane, signature);
8030
8071
  if (signature.unique && seen.payload > 0)
8031
8072
  return "unverifiable";
@@ -8042,6 +8083,11 @@ export class Daemon extends EventEmitter {
8042
8083
  && !this.backend.getBusyPattern?.()?.test(pane)) {
8043
8084
  return after.payload > 0 ? "unverifiable" : "unproven";
8044
8085
  }
8086
+ // A structural reader (claude-code) that finds no input box on this screen — a dialog in front of it, a layout it
8087
+ // does not know — cannot place our text on either side of the box: the same verdicts as a readerless backend.
8088
+ if (this.backend?.readInputRow && !after.inputReadable) {
8089
+ return after.payload > 0 ? "unverifiable" : "unproven";
8090
+ }
8045
8091
  // 1. Disqualifying evidence, checked FIRST and never overridden by the
8046
8092
  // corroborating evidence below: our text is sitting in the input row, so
8047
8093
  // it was not submitted — whatever else is on screen.
@@ -8052,8 +8098,22 @@ export class Daemon extends EventEmitter {
8052
8098
  // Otherwise an older stranded message with the same opening would be
8053
8099
  // read as ours, we would press Enter to "recover" it, and the turn IT
8054
8100
  // starts would confirm a message that never reached the pane.
8055
- if (after.strandedInput && (signature.unique || baseline?.strandedInput === false))
8101
+ //
8102
+ // For a structural box reader (claude-code), "did not already show it" needs a baseline whose box was READ: an
8103
+ // unreadable one (a dialog, a redraw the reader refused) says nothing about what the box held, and counting it as
8104
+ // empty would claim an older message as ours and press Enter on it (#1353 review). The prompt-row backends (codex,
8105
+ // kiro) keep their existing attribution.
8106
+ const attributable = baseline != null
8107
+ && (!this.backend?.readInputRow || (baseline.inputReadable && after.inputReadable));
8108
+ if (after.strandedInput && (signature.unique || (attributable && baseline.strandedInput === false)))
8109
+ return "stranded";
8110
+ // A paste the CLI shows collapsed carries no signature at all. One the box did not hold before we pasted is
8111
+ // ours, and it is still in the box — whatever echo or output is on screen (#1200).
8112
+ if (attributable && after.collapsedPastes > baseline.collapsedPastes)
8056
8113
  return "stranded";
8114
+ // Something sits in the box that cannot be attributed either way. Only a unique signature seen outside the box
8115
+ // (below) can still prove this delivery; nothing else on screen may.
8116
+ const unattributedResidue = !attributable && (after.strandedInput || after.collapsedPastes > 0);
8057
8117
  // 2. Positive evidence. A unique signature needs no baseline: no earlier
8058
8118
  // message can carry this delivery's message_id, so finding it outside
8059
8119
  // the input row is proof in itself — which also means a momentary
@@ -8061,12 +8121,16 @@ export class Daemon extends EventEmitter {
8061
8121
  // message into a re-paste.
8062
8122
  if (signature.unique && after.payload > 0)
8063
8123
  return this.submittedProof();
8124
+ if (unattributedResidue)
8125
+ return "unverifiable";
8064
8126
  // 3. Otherwise the evidence must be NEW relative to the pane as it was
8065
8127
  // before we pasted: a queue marker left by an earlier message, or an
8066
8128
  // older transcript entry that opens the same way, are on screen either
8067
8129
  // way and would otherwise confirm a paste that never landed.
8068
8130
  if (baseline) {
8069
- if (after.queued > baseline.queued)
8131
+ // A queue is new only against a box that was read before the paste (a reader backend): an unreadable baseline
8132
+ // says nothing about whether the queue was already there.
8133
+ if (after.queued > baseline.queued && (!this.backend?.readInputRow || baseline.inputReadable))
8070
8134
  return this.submittedProof();
8071
8135
  if (after.payload > baseline.payload)
8072
8136
  return this.submittedProof();
@@ -8094,12 +8158,10 @@ export class Daemon extends EventEmitter {
8094
8158
  return proof;
8095
8159
  if (signature.unique && this.tmux?.capturePaneWithHistory) {
8096
8160
  try {
8161
+ // Scrollback can hold the echo the viewport lost — but it is judged exactly like the viewport: a box that
8162
+ // cannot be read, or that still holds our text, is never upgraded by an echo further up.
8097
8163
  const history = await this.tmux.capturePaneWithHistory(300);
8098
- const seen = this.paneEvidence(history, signature);
8099
- const known = !this.backend?.isDeliveryInputReadyPane
8100
- || this.backend.isDeliveryInputReadyPane(history)
8101
- || this.backend.getBusyPattern?.()?.test(history);
8102
- if (known && seen.payload > 0 && !seen.strandedInput)
8164
+ if (this.judgeSubmission(history, signature, baseline) === "submitted")
8103
8165
  return this.submittedProof();
8104
8166
  }
8105
8167
  catch { /* a failed history read proves neither delivery nor loss */ }
@@ -8123,9 +8185,8 @@ export class Daemon extends EventEmitter {
8123
8185
  try {
8124
8186
  const pane = await this.tmux.capturePane();
8125
8187
  const evidence = this.paneEvidence(pane, signature);
8126
- const prompt = this.backend?.getBottomReadyPattern?.();
8127
- if (prompt && (!this.backend?.isDeliveryInputReadyPane || this.backend.isDeliveryInputReadyPane(pane))
8128
- && strandedAgendMessageInInput(pane, prompt)) {
8188
+ const input = this.inputRegion(pane);
8189
+ if (input && agendMessageInInput(input.text)) {
8129
8190
  // Whatever we paste now lands after it, and one Enter submits both as
8130
8191
  // a single message. Nothing here can undo that; saying so beats
8131
8192
  // letting two messages silently merge.
@@ -8144,16 +8205,45 @@ export class Daemon extends EventEmitter {
8144
8205
  return null;
8145
8206
  }
8146
8207
  paneEvidence(pane, signature) {
8147
- const marker = this.backend?.getQueuedInputMarker?.();
8148
- const prompt = this.backend?.getBottomReadyPattern?.();
8149
- const input = prompt && (!this.backend?.isDeliveryInputReadyPane || this.backend.isDeliveryInputReadyPane(pane))
8150
- ? inputAreaText(pane, prompt) : null;
8208
+ const input = this.inputRegion(pane);
8209
+ // A structural box reader vouches for the queue itself (InputBox.queued): a marker's words elsewhere on the pane —
8210
+ // a reply quoting it, an old block — are not a queue (#1169 review). Other backends count their marker rows.
8211
+ const marker = this.backend?.readInputRow ? null : this.backend?.getQueuedInputMarker?.();
8151
8212
  return {
8152
- queued: marker ? pane.split(/\r?\n/).filter(row => marker.test(row)).length : 0,
8213
+ queued: this.backend?.readInputRow
8214
+ ? (input?.queued ? 1 : 0)
8215
+ : marker ? pane.split(/\r?\n/).filter(row => marker.test(row)).length : 0,
8153
8216
  payload: countOccurrences(pane.replace(/\s+/g, ""), signature.value),
8154
- strandedInput: input != null && inputShowsPastedText(input, signature.value),
8217
+ strandedInput: input != null && inputShowsPastedText(input.text, signature.value),
8218
+ collapsedPastes: input?.collapsedPastes ?? 0,
8219
+ inputReadable: input != null,
8155
8220
  };
8156
8221
  }
8222
+ /**
8223
+ * The input box as it stands on this screen: the backend's structural reader when it has one (claude-code, #1200),
8224
+ * else the prompt-row heuristic (codex — only on a screen it vouches for — and kiro). null: no box to read here.
8225
+ */
8226
+ inputRegion(pane) {
8227
+ if (this.backend?.readInputRow)
8228
+ return this.backend.readInputRow(pane);
8229
+ const prompt = this.backend?.getBottomReadyPattern?.();
8230
+ if (!prompt || (this.backend?.isDeliveryInputReadyPane && !this.backend.isDeliveryInputReadyPane(pane)))
8231
+ return null;
8232
+ const text = inputAreaText(pane, prompt);
8233
+ return text == null ? null : { text, collapsedPastes: 0 };
8234
+ }
8235
+ /** Whether this backend's input box can be read at all (a given screen may still have none). */
8236
+ readsInput() {
8237
+ return !!this.backend?.readInputRow || !!this.backend?.getBottomReadyPattern?.();
8238
+ }
8239
+ /**
8240
+ * Backends whose pane evidence is structured (codex's input/footer pair, a structural box reader): there, our text
8241
+ * missing from the viewport is inconclusive — an echo can arrive late or scroll away — and never a loss to re-paste
8242
+ * over. Only text positively seen in the box (a strand) authorizes another Enter.
8243
+ */
8244
+ structuredInputEvidence() {
8245
+ return !!this.backend?.isDeliveryInputReadyPane || !!this.backend?.readInputRow;
8246
+ }
8157
8247
  /**
8158
8248
  * What identifies THIS message on screen. The routing envelope carries a
8159
8249
  * message_id that no earlier transcript entry can share, so prefer it: the
@@ -8220,6 +8310,17 @@ export class Daemon extends EventEmitter {
8220
8310
  if (!(await this.sendDeliveryEnter(label, guard?.current)))
8221
8311
  return false;
8222
8312
  let proof = await this.confirmSubmitted(signature, baseline);
8313
+ // A structural box reader sees the paste in the box until the CLI digests the Enter (claude-code repaints a moment
8314
+ // later): give that a bounded window before calling it stranded or lost, as the delivery ladder does.
8315
+ if (this.backend?.readInputRow) {
8316
+ const deadline = Date.now() + POST_ENTER_PROOF_WINDOW_MS;
8317
+ while (proof !== "submitted" && Date.now() < deadline) {
8318
+ await new Promise(r => setTimeout(r, POST_ENTER_PROOF_POLL_MS));
8319
+ if (guard && !guard.current())
8320
+ return false;
8321
+ proof = await this.confirmSubmitted(signature, baseline);
8322
+ }
8323
+ }
8223
8324
  if (proof === "unverifiable") {
8224
8325
  if (this.backend?.isDeliveryInputReadyPane) {
8225
8326
  // A Codex screen without a structurally current input/footer pair is