@cluesmith/codev 3.3.3 → 3.3.5

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 (244) hide show
  1. package/dashboard-dist/assets/index-BuflRUi6.js +220 -0
  2. package/dashboard-dist/assets/index-BuflRUi6.js.map +1 -0
  3. package/dashboard-dist/assets/index-r3bZzLmX.css +32 -0
  4. package/dashboard-dist/index.html +2 -2
  5. package/dist/agent-farm/cli.d.ts.map +1 -1
  6. package/dist/agent-farm/cli.js +48 -0
  7. package/dist/agent-farm/cli.js.map +1 -1
  8. package/dist/agent-farm/commands/ide.d.ts +20 -0
  9. package/dist/agent-farm/commands/ide.d.ts.map +1 -0
  10. package/dist/agent-farm/commands/ide.js +138 -0
  11. package/dist/agent-farm/commands/ide.js.map +1 -0
  12. package/dist/agent-farm/commands/inbox.d.ts.map +1 -1
  13. package/dist/agent-farm/commands/inbox.js +15 -3
  14. package/dist/agent-farm/commands/inbox.js.map +1 -1
  15. package/dist/agent-farm/commands/send.d.ts +3 -2
  16. package/dist/agent-farm/commands/send.d.ts.map +1 -1
  17. package/dist/agent-farm/commands/send.js +40 -6
  18. package/dist/agent-farm/commands/send.js.map +1 -1
  19. package/dist/agent-farm/commands/spawn-worktree.d.ts.map +1 -1
  20. package/dist/agent-farm/commands/spawn-worktree.js +28 -9
  21. package/dist/agent-farm/commands/spawn-worktree.js.map +1 -1
  22. package/dist/agent-farm/commands/tower.d.ts +21 -9
  23. package/dist/agent-farm/commands/tower.d.ts.map +1 -1
  24. package/dist/agent-farm/commands/tower.js +90 -30
  25. package/dist/agent-farm/commands/tower.js.map +1 -1
  26. package/dist/agent-farm/db/tower-owner.d.ts +168 -0
  27. package/dist/agent-farm/db/tower-owner.d.ts.map +1 -0
  28. package/dist/agent-farm/db/tower-owner.js +369 -0
  29. package/dist/agent-farm/db/tower-owner.js.map +1 -0
  30. package/dist/agent-farm/db/types.d.ts +30 -5
  31. package/dist/agent-farm/db/types.d.ts.map +1 -1
  32. package/dist/agent-farm/db/types.js.map +1 -1
  33. package/dist/agent-farm/lib/ide-record.d.ts +65 -0
  34. package/dist/agent-farm/lib/ide-record.d.ts.map +1 -0
  35. package/dist/agent-farm/lib/ide-record.js +118 -0
  36. package/dist/agent-farm/lib/ide-record.js.map +1 -0
  37. package/dist/agent-farm/lib/tunnel-client.d.ts +22 -0
  38. package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -1
  39. package/dist/agent-farm/lib/tunnel-client.js +53 -5
  40. package/dist/agent-farm/lib/tunnel-client.js.map +1 -1
  41. package/dist/agent-farm/servers/decisions/file.d.ts +50 -0
  42. package/dist/agent-farm/servers/decisions/file.d.ts.map +1 -0
  43. package/dist/agent-farm/servers/decisions/file.js +146 -0
  44. package/dist/agent-farm/servers/decisions/file.js.map +1 -0
  45. package/dist/agent-farm/servers/decisions/grammar.d.ts +36 -0
  46. package/dist/agent-farm/servers/decisions/grammar.d.ts.map +1 -0
  47. package/dist/agent-farm/servers/decisions/grammar.js +109 -0
  48. package/dist/agent-farm/servers/decisions/grammar.js.map +1 -0
  49. package/dist/agent-farm/servers/decisions/notice.d.ts +11 -0
  50. package/dist/agent-farm/servers/decisions/notice.d.ts.map +1 -0
  51. package/dist/agent-farm/servers/decisions/notice.js +18 -0
  52. package/dist/agent-farm/servers/decisions/notice.js.map +1 -0
  53. package/dist/agent-farm/servers/decisions/parse.d.ts +36 -0
  54. package/dist/agent-farm/servers/decisions/parse.d.ts.map +1 -0
  55. package/dist/agent-farm/servers/decisions/parse.js +308 -0
  56. package/dist/agent-farm/servers/decisions/parse.js.map +1 -0
  57. package/dist/agent-farm/servers/decisions/routes.d.ts +17 -0
  58. package/dist/agent-farm/servers/decisions/routes.d.ts.map +1 -0
  59. package/dist/agent-farm/servers/decisions/routes.js +160 -0
  60. package/dist/agent-farm/servers/decisions/routes.js.map +1 -0
  61. package/dist/agent-farm/servers/decisions/splice.d.ts +26 -0
  62. package/dist/agent-farm/servers/decisions/splice.d.ts.map +1 -0
  63. package/dist/agent-farm/servers/decisions/splice.js +120 -0
  64. package/dist/agent-farm/servers/decisions/splice.js.map +1 -0
  65. package/dist/agent-farm/servers/decisions/stamp.d.ts +4 -0
  66. package/dist/agent-farm/servers/decisions/stamp.d.ts.map +1 -0
  67. package/dist/agent-farm/servers/decisions/stamp.js +171 -0
  68. package/dist/agent-farm/servers/decisions/stamp.js.map +1 -0
  69. package/dist/agent-farm/servers/gate-profiles.d.ts +23 -2
  70. package/dist/agent-farm/servers/gate-profiles.d.ts.map +1 -1
  71. package/dist/agent-farm/servers/gate-profiles.js +43 -2
  72. package/dist/agent-farm/servers/gate-profiles.js.map +1 -1
  73. package/dist/agent-farm/servers/ide-forward.d.ts +62 -0
  74. package/dist/agent-farm/servers/ide-forward.d.ts.map +1 -0
  75. package/dist/agent-farm/servers/ide-forward.js +274 -0
  76. package/dist/agent-farm/servers/ide-forward.js.map +1 -0
  77. package/dist/agent-farm/servers/ide-server.d.ts +68 -0
  78. package/dist/agent-farm/servers/ide-server.d.ts.map +1 -0
  79. package/dist/agent-farm/servers/ide-server.js +281 -0
  80. package/dist/agent-farm/servers/ide-server.js.map +1 -0
  81. package/dist/agent-farm/servers/mailbox-delivery.d.ts +213 -4
  82. package/dist/agent-farm/servers/mailbox-delivery.d.ts.map +1 -1
  83. package/dist/agent-farm/servers/mailbox-delivery.js +489 -68
  84. package/dist/agent-farm/servers/mailbox-delivery.js.map +1 -1
  85. package/dist/agent-farm/servers/mailbox-wiring.d.ts +18 -4
  86. package/dist/agent-farm/servers/mailbox-wiring.d.ts.map +1 -1
  87. package/dist/agent-farm/servers/mailbox-wiring.js +94 -21
  88. package/dist/agent-farm/servers/mailbox-wiring.js.map +1 -1
  89. package/dist/agent-farm/servers/message-write.d.ts +138 -11
  90. package/dist/agent-farm/servers/message-write.d.ts.map +1 -1
  91. package/dist/agent-farm/servers/message-write.js +144 -35
  92. package/dist/agent-farm/servers/message-write.js.map +1 -1
  93. package/dist/agent-farm/servers/overview.d.ts +32 -15
  94. package/dist/agent-farm/servers/overview.d.ts.map +1 -1
  95. package/dist/agent-farm/servers/overview.js +181 -103
  96. package/dist/agent-farm/servers/overview.js.map +1 -1
  97. package/dist/agent-farm/servers/render-gate.d.ts +53 -0
  98. package/dist/agent-farm/servers/render-gate.d.ts.map +1 -1
  99. package/dist/agent-farm/servers/render-gate.js +135 -24
  100. package/dist/agent-farm/servers/render-gate.js.map +1 -1
  101. package/dist/agent-farm/servers/session-submit.d.ts +7 -4
  102. package/dist/agent-farm/servers/session-submit.d.ts.map +1 -1
  103. package/dist/agent-farm/servers/session-submit.js +7 -4
  104. package/dist/agent-farm/servers/session-submit.js.map +1 -1
  105. package/dist/agent-farm/servers/tower-cron.d.ts.map +1 -1
  106. package/dist/agent-farm/servers/tower-cron.js +4 -1
  107. package/dist/agent-farm/servers/tower-cron.js.map +1 -1
  108. package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -1
  109. package/dist/agent-farm/servers/tower-instances.js +6 -7
  110. package/dist/agent-farm/servers/tower-instances.js.map +1 -1
  111. package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
  112. package/dist/agent-farm/servers/tower-routes.js +116 -18
  113. package/dist/agent-farm/servers/tower-routes.js.map +1 -1
  114. package/dist/agent-farm/servers/tower-server.js +64 -7
  115. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  116. package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
  117. package/dist/agent-farm/servers/tower-terminals.js +53 -8
  118. package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
  119. package/dist/agent-farm/servers/tower-websocket.d.ts.map +1 -1
  120. package/dist/agent-farm/servers/tower-websocket.js +18 -1
  121. package/dist/agent-farm/servers/tower-websocket.js.map +1 -1
  122. package/dist/agent-farm/utils/message-format.d.ts +8 -0
  123. package/dist/agent-farm/utils/message-format.d.ts.map +1 -1
  124. package/dist/agent-farm/utils/message-format.js +21 -6
  125. package/dist/agent-farm/utils/message-format.js.map +1 -1
  126. package/dist/agent-farm/utils/port.d.ts +22 -0
  127. package/dist/agent-farm/utils/port.d.ts.map +1 -0
  128. package/dist/agent-farm/utils/port.js +35 -0
  129. package/dist/agent-farm/utils/port.js.map +1 -0
  130. package/dist/agent-farm/utils/server-utils.d.ts +26 -0
  131. package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
  132. package/dist/agent-farm/utils/server-utils.js +36 -0
  133. package/dist/agent-farm/utils/server-utils.js.map +1 -1
  134. package/dist/cli.js +2 -2
  135. package/dist/cli.js.map +1 -1
  136. package/dist/commands/consult/index.d.ts +20 -1
  137. package/dist/commands/consult/index.d.ts.map +1 -1
  138. package/dist/commands/consult/index.js +117 -3
  139. package/dist/commands/consult/index.js.map +1 -1
  140. package/dist/commands/consult/tree-tripwire.d.ts +177 -0
  141. package/dist/commands/consult/tree-tripwire.d.ts.map +1 -0
  142. package/dist/commands/consult/tree-tripwire.js +356 -0
  143. package/dist/commands/consult/tree-tripwire.js.map +1 -0
  144. package/dist/commands/doctor.d.ts +69 -0
  145. package/dist/commands/doctor.d.ts.map +1 -1
  146. package/dist/commands/doctor.js +163 -0
  147. package/dist/commands/doctor.js.map +1 -1
  148. package/dist/commands/generate-image.d.ts +5 -0
  149. package/dist/commands/generate-image.d.ts.map +1 -1
  150. package/dist/commands/generate-image.js +225 -4
  151. package/dist/commands/generate-image.js.map +1 -1
  152. package/dist/commands/porch/checks.d.ts +7 -1
  153. package/dist/commands/porch/checks.d.ts.map +1 -1
  154. package/dist/commands/porch/checks.js +77 -14
  155. package/dist/commands/porch/checks.js.map +1 -1
  156. package/dist/commands/porch/index.d.ts +38 -1
  157. package/dist/commands/porch/index.d.ts.map +1 -1
  158. package/dist/commands/porch/index.js +79 -43
  159. package/dist/commands/porch/index.js.map +1 -1
  160. package/dist/commands/porch/protocol.d.ts +4 -3
  161. package/dist/commands/porch/protocol.d.ts.map +1 -1
  162. package/dist/commands/porch/protocol.js +32 -5
  163. package/dist/commands/porch/protocol.js.map +1 -1
  164. package/dist/commands/porch/types.d.ts +2 -0
  165. package/dist/commands/porch/types.d.ts.map +1 -1
  166. package/dist/lib/agent-env.d.ts +95 -0
  167. package/dist/lib/agent-env.d.ts.map +1 -0
  168. package/dist/lib/agent-env.js +137 -0
  169. package/dist/lib/agent-env.js.map +1 -0
  170. package/dist/lib/config.d.ts +8 -0
  171. package/dist/lib/config.d.ts.map +1 -1
  172. package/dist/lib/config.js.map +1 -1
  173. package/dist/lib/forge-contracts.d.ts +10 -0
  174. package/dist/lib/forge-contracts.d.ts.map +1 -1
  175. package/dist/lib/forge-contracts.js.map +1 -1
  176. package/dist/lib/forge-rate-limit.d.ts +45 -0
  177. package/dist/lib/forge-rate-limit.d.ts.map +1 -0
  178. package/dist/lib/forge-rate-limit.js +89 -0
  179. package/dist/lib/forge-rate-limit.js.map +1 -0
  180. package/dist/lib/forge.d.ts +28 -0
  181. package/dist/lib/forge.d.ts.map +1 -1
  182. package/dist/lib/forge.js +63 -10
  183. package/dist/lib/forge.js.map +1 -1
  184. package/dist/lib/github.d.ts +13 -1
  185. package/dist/lib/github.d.ts.map +1 -1
  186. package/dist/lib/github.js +58 -15
  187. package/dist/lib/github.js.map +1 -1
  188. package/dist/lib/test-env.d.ts +17 -0
  189. package/dist/lib/test-env.d.ts.map +1 -1
  190. package/dist/lib/test-env.js +20 -0
  191. package/dist/lib/test-env.js.map +1 -1
  192. package/dist/terminal/pty-session.d.ts +122 -4
  193. package/dist/terminal/pty-session.d.ts.map +1 -1
  194. package/dist/terminal/pty-session.js +142 -7
  195. package/dist/terminal/pty-session.js.map +1 -1
  196. package/dist/terminal/session-manager.d.ts +23 -2
  197. package/dist/terminal/session-manager.d.ts.map +1 -1
  198. package/dist/terminal/session-manager.js +115 -36
  199. package/dist/terminal/session-manager.js.map +1 -1
  200. package/dist/terminal/session-screen.d.ts +34 -5
  201. package/dist/terminal/session-screen.d.ts.map +1 -1
  202. package/dist/terminal/session-screen.js +52 -6
  203. package/dist/terminal/session-screen.js.map +1 -1
  204. package/dist/terminal/terminal-replies.d.ts +102 -0
  205. package/dist/terminal/terminal-replies.d.ts.map +1 -0
  206. package/dist/terminal/terminal-replies.js +181 -0
  207. package/dist/terminal/terminal-replies.js.map +1 -0
  208. package/package.json +4 -4
  209. package/scripts/forge/github/pr-list.sh +6 -2
  210. package/scripts/forge/github/pr-search.sh +4 -2
  211. package/scripts/forge/gitlab/issue-search.sh +1 -0
  212. package/scripts/forge/gitlab/pr-search.sh +35 -2
  213. package/scripts/forge/linear/auth-status.sh +1 -0
  214. package/scripts/forge/linear/issue-comment.sh +1 -0
  215. package/scripts/forge/linear/issue-list.sh +1 -0
  216. package/scripts/forge/linear/issue-search.sh +1 -0
  217. package/scripts/forge/linear/issue-view.sh +1 -0
  218. package/scripts/forge/linear/recently-closed.sh +1 -0
  219. package/scripts/forge/linear/user-identity.sh +1 -0
  220. package/skeleton/.claude/skills/afx/SKILL.md +15 -0
  221. package/skeleton/.claude/skills/arch-init/SKILL.md +56 -9
  222. package/skeleton/.claude/skills/arch-save/SKILL.md +100 -9
  223. package/skeleton/.claude/skills/consult/SKILL.md +9 -0
  224. package/skeleton/.claude/skills/generate-image/SKILL.md +19 -2
  225. package/skeleton/.claude/skills/porch/SKILL.md +18 -0
  226. package/skeleton/.codex/skills/afx/SKILL.md +15 -0
  227. package/skeleton/.codex/skills/arch-init/SKILL.md +56 -9
  228. package/skeleton/.codex/skills/arch-save/SKILL.md +100 -9
  229. package/skeleton/.codex/skills/consult/SKILL.md +9 -0
  230. package/skeleton/.codex/skills/generate-image/SKILL.md +19 -2
  231. package/skeleton/.codex/skills/porch/SKILL.md +18 -0
  232. package/skeleton/protocol-schema.json +6 -0
  233. package/skeleton/protocols/maintain/protocol.md +5 -0
  234. package/skeleton/protocols/pir/prompts/review.md +0 -1
  235. package/skeleton/protocols/protocol-schema.json +6 -0
  236. package/skeleton/resources/commands/agent-farm.md +71 -2
  237. package/skeleton/resources/commands/consult.md +9 -2
  238. package/skeleton/roles/consultant.md +32 -0
  239. package/skeleton/templates/AGENTS.md +1 -1
  240. package/skeleton/templates/CLAUDE.md +1 -1
  241. package/skeleton/templates/cheatsheet.md +1 -1
  242. package/dashboard-dist/assets/index-CUIjS_Vb.js +0 -199
  243. package/dashboard-dist/assets/index-CUIjS_Vb.js.map +0 -1
  244. package/dashboard-dist/assets/index-DnYLy5C_.css +0 -32
@@ -1,17 +1,113 @@
1
1
  /**
2
- * Paced message writing for PTY sessions (Bugfix #584).
2
+ * Paced message writing for PTY sessions (Bugfix #584, Issue #1567).
3
3
  *
4
4
  * Extracted to a shared module to avoid circular imports between
5
5
  * tower-routes.ts and tower-cron.ts.
6
6
  */
7
7
  import { trySubmitToSession, watchBypasses } from './session-submit.js';
8
- // Messages longer than this threshold are written line-by-line with delays
9
- // to prevent the receiving terminal from classifying the input as a paste
10
- // and swallowing the final Enter.
11
- const PACED_WRITE_LINE_THRESHOLD = 4;
12
- const INTER_LINE_DELAY_MS = 10;
13
- const PACED_ENTER_DELAY_MS = 80;
14
8
  const SIMPLE_ENTER_DELAY_MS = 50;
9
+ /**
10
+ * Issue #1567 — why long frames are no longer handed to the PTY in one write.
11
+ *
12
+ * The kernel splits a large master-side write at the slave's input-queue high-water mark
13
+ * (TTYHOG − 2 = 1022 bytes on macOS). The receiving TUI reads the first ~1 KB as one chunk,
14
+ * its paste heuristic classifies that chunk as a paste and DISCARDS it, then types the
15
+ * remainder and the Enter normally — so the agent saw a mid-sentence tail-only fragment
16
+ * while every `write()` reported success. Measured on claude 2.1.263 (harness in
17
+ * `scripts/bugfix-1567-head-loss-harness.mts`, evidence in `codev/evidence/1567-head-loss/`):
18
+ * a 1,172-byte 3-line frame lost its head 14/20 times on an IDLE composer; a 1,000-byte frame
19
+ * never did. Two independent changes each measured 0 losses, and both are applied:
20
+ *
21
+ * 1. **No single write exceeds {@link PASTE_CHUNK_BYTES}** — comfortably under the queue
22
+ * limit, so the kernel never splits a read and the reader drains between writes.
23
+ * 2. **Long frames travel as ONE explicit bracketed paste** (`ESC[200~` … `ESC[201~`), so the
24
+ * TUI's paste heuristic never runs on chunk boundaries and newlines inside the body can
25
+ * never be read as Enter. The Enter is a separate write OUTSIDE the bracket, after a
26
+ * settle. Inside the bracket newlines travel as `\r`, which is what a terminal emulator
27
+ * emits on paste (xterm.js) and what claude's composer expects there — the same convention
28
+ * `apps/vscode/src/review-queue/queue.ts` already uses to type a review into a builder.
29
+ *
30
+ * This replaces the #584-era per-line pacing for frames of {@link BRACKET_MIN_LINES} lines or
31
+ * more, which was tuned against an older TUI's heuristic. Short frames keep the pre-existing
32
+ * single write + delayed Enter, byte for byte, so `--raw` slash commands and short sends are
33
+ * untouched.
34
+ */
35
+ export const PASTE_CHUNK_BYTES = 512;
36
+ /** Gap between consecutive chunk writes; the reader drains a chunk in well under this. */
37
+ export const PASTE_CHUNK_GAP_MS = 5;
38
+ /** Settle between the paste's closing marker and the Enter that submits it (measured: 0/29 losses). */
39
+ export const PASTE_ENTER_DELAY_MS = 80;
40
+ /**
41
+ * Frames LONGER than this many bytes, or of {@link BRACKET_MIN_LINES} lines or more, are
42
+ * bracketed and chunked. Well under the 1,022-byte queue limit where the loss starts: the
43
+ * field's smallest confirmed truncations were ~500-byte bodies, and a bracketed short paste
44
+ * costs nothing on the measured harnesses.
45
+ */
46
+ export const BRACKET_MIN_BYTES = 256;
47
+ /** The #584 threshold, kept: any frame of this many lines or more is never typed line by line. */
48
+ export const BRACKET_MIN_LINES = 4;
49
+ /** Gap between line pieces on the non-bracketed (opt-out) strategy — the #584 pacing. */
50
+ const INTER_LINE_DELAY_MS = 10;
51
+ export const PASTE_BEGIN = '\x1b[200~';
52
+ export const PASTE_END = '\x1b[201~';
53
+ export const BRACKETED_PASTE = { bracketedPaste: true };
54
+ export const PLAIN_CHUNKED = { bracketedPaste: false };
55
+ /**
56
+ * The write strategy for a gate profile's `app`. agy is the one modeled harness whose paste
57
+ * handling was not measurable (unauthenticated), so it keeps the per-line shape; anything
58
+ * else is bracketed. Unknown apps never reach the write edge (the gate holds `no-profile`).
59
+ */
60
+ export function writeStrategyForApp(app) {
61
+ return app === 'agy' ? PLAIN_CHUNKED : BRACKETED_PASTE;
62
+ }
63
+ /** Does this frame take the bracketed/chunked path rather than the single short write? */
64
+ export function isLongFrame(message) {
65
+ return message.split('\n').length >= BRACKET_MIN_LINES || Buffer.byteLength(message, 'utf8') > BRACKET_MIN_BYTES;
66
+ }
67
+ /**
68
+ * Split `text` into pieces of at most `max` bytes, never inside a UTF-8 sequence (a split
69
+ * continuation byte would reach the TUI as two invalid characters).
70
+ */
71
+ export function chunkForPty(text, max = PASTE_CHUNK_BYTES) {
72
+ const buf = Buffer.from(text, 'utf8');
73
+ const out = [];
74
+ for (let at = 0; at < buf.length;) {
75
+ let end = Math.min(buf.length, at + max);
76
+ while (end < buf.length && end > at && (buf[end] & 0xc0) === 0x80)
77
+ end--;
78
+ out.push(buf.subarray(at, end).toString('utf8'));
79
+ at = end;
80
+ }
81
+ return out;
82
+ }
83
+ /**
84
+ * The pieces of a long frame, in write order, for a strategy. Bracketed: the body (newlines
85
+ * as `\r`) chunked, with the opening marker on the first piece and the closing marker on the
86
+ * last — the markers are never split across writes, so no chunk boundary can land inside an
87
+ * escape sequence, and every piece INCLUDING its marker stays within {@link PASTE_CHUNK_BYTES}.
88
+ * Plain: one piece per line (`\n` kept, as #584 wrote it), with any line over
89
+ * the chunk size split further.
90
+ */
91
+ export function framePieces(message, strategy) {
92
+ if (strategy.bracketedPaste) {
93
+ // A literal paste marker inside the body (a pasted terminal log, say) would end or restart
94
+ // the paste mid-frame and let the remainder be typed as keys; strip both so the bracket
95
+ // we add is the only one the TUI sees.
96
+ const body = message.replace(/\r?\n/g, '\r').split(PASTE_BEGIN).join('').split(PASTE_END).join('');
97
+ // Chunk with room for the markers so NO write — first or last piece included — exceeds
98
+ // PASTE_CHUNK_BYTES on the wire.
99
+ const pieces = chunkForPty(body, PASTE_CHUNK_BYTES - PASTE_BEGIN.length - PASTE_END.length);
100
+ // A body that was NOTHING but markers is empty after stripping; still emit one (empty) paste
101
+ // so the frame is a well-formed no-op rather than the literal text "undefined".
102
+ if (pieces.length === 0)
103
+ pieces.push('');
104
+ pieces[0] = PASTE_BEGIN + pieces[0];
105
+ pieces[pieces.length - 1] += PASTE_END;
106
+ return pieces;
107
+ }
108
+ const lines = message.split('\n');
109
+ return lines.flatMap((line, i) => chunkForPty(i < lines.length - 1 ? line + '\n' : line));
110
+ }
15
111
  /** ESC keystroke — ends the agent's current turn (Spec 1273). */
16
112
  export const ESC = '\x1b';
17
113
  /**
@@ -43,21 +139,22 @@ export function writeEscapeToSession(session, noEnter) {
43
139
  return ESCAPE_ENTER_DELAY_MS;
44
140
  }
45
141
  /**
46
- * Write a message to a PTY session, pacing multi-line output to prevent
47
- * the terminal from treating it as a paste (Bugfix #584).
142
+ * Write a message to a PTY session (Bugfix #584, Issue #1567).
143
+ *
144
+ * Short frames (under {@link BRACKET_MIN_LINES} lines AND at most {@link BRACKET_MIN_BYTES}
145
+ * bytes): a single write, then Enter after 50 ms — unchanged since #584.
48
146
  *
49
- * Short messages (≤3 lines): single write + delayed Enter.
50
- * Long messages (>3 lines): line-by-line writes with 10ms gaps, then Enter
51
- * after all lines are delivered.
147
+ * Long frames: {@link framePieces} written {@link PASTE_CHUNK_GAP_MS} apart (bracketed by
148
+ * default; see {@link WriteStrategy}), then Enter as its own write
149
+ * {@link PASTE_ENTER_DELAY_MS} after the last piece. The Enter is always outside the paste
150
+ * bracket, so a TUI that collapses the paste into a placard still submits it.
52
151
  *
53
152
  * @param delayOffset ms offset for all scheduled writes (used to serialize
54
153
  * multiple messages to the same session without interleaving)
55
154
  * @returns ms timestamp (from call time) when all writes complete
56
155
  */
57
- export function writeMessageToSession(session, message, noEnter, delayOffset = 0) {
58
- const lines = message.split('\n');
59
- if (lines.length < PACED_WRITE_LINE_THRESHOLD) {
60
- // Short messages: single write (existing behavior, works fine)
156
+ export function writeMessageToSession(session, message, noEnter, delayOffset = 0, strategy = BRACKETED_PASTE) {
157
+ if (!isLongFrame(message)) {
61
158
  if (delayOffset === 0) {
62
159
  session.write(message);
63
160
  }
@@ -70,26 +167,23 @@ export function writeMessageToSession(session, message, noEnter, delayOffset = 0
70
167
  }
71
168
  return enterTime;
72
169
  }
73
- // Multi-line: pace output line-by-line to avoid paste detection.
74
- // Writing all lines in a single write() causes the terminal to treat it
75
- // as a paste, swallowing the final Enter.
76
- for (let i = 0; i < lines.length; i++) {
77
- const text = i < lines.length - 1 ? lines[i] + '\n' : lines[i];
78
- const lineDelay = delayOffset + i * INTER_LINE_DELAY_MS;
79
- if (lineDelay === 0) {
80
- session.write(text);
170
+ const pieces = framePieces(message, strategy);
171
+ const gap = strategy.bracketedPaste ? PASTE_CHUNK_GAP_MS : INTER_LINE_DELAY_MS;
172
+ for (let i = 0; i < pieces.length; i++) {
173
+ const at = delayOffset + i * gap;
174
+ if (at === 0) {
175
+ session.write(pieces[i]);
81
176
  }
82
177
  else {
83
- setTimeout(() => session.write(text), lineDelay);
178
+ setTimeout(() => session.write(pieces[i]), at);
84
179
  }
85
180
  }
86
- const lastLineTime = delayOffset + (lines.length - 1) * INTER_LINE_DELAY_MS;
87
- if (!noEnter) {
88
- const enterTime = lastLineTime + PACED_ENTER_DELAY_MS;
89
- setTimeout(() => session.write('\r'), enterTime);
90
- return enterTime;
91
- }
92
- return lastLineTime;
181
+ const lastPieceTime = delayOffset + (pieces.length - 1) * gap;
182
+ if (noEnter)
183
+ return lastPieceTime;
184
+ const enterTime = lastPieceTime + PASTE_ENTER_DELAY_MS;
185
+ setTimeout(() => session.write('\r'), enterTime);
186
+ return enterTime;
93
187
  }
94
188
  /**
95
189
  * Paced write of a message (text + trailing Enter unless `noEnter`) performed as ONE
@@ -117,7 +211,7 @@ export function writeMessageToSession(session, message, noEnter, delayOffset = 0
117
211
  * `submitToSession` schedules its own equal-offset sleep, so the Enter still executes
118
212
  * first — which is what makes the per-agent serializer's completion-chaining real.
119
213
  */
120
- export async function submitMessagePaced(session, message, noEnter, precheck, clock) {
214
+ export async function submitMessagePaced(session, message, noEnter, precheck, clock, strategy = BRACKETED_PASTE) {
121
215
  // Fail LOUD on a missing id rather than keying the lock on `undefined`. Sessions reach
122
216
  // this through structurally-typed ports, so a double without an id compiles fine and
123
217
  // would silently collapse every per-terminal lock into one global lock — serialization
@@ -137,9 +231,17 @@ export async function submitMessagePaced(session, message, noEnter, precheck, cl
137
231
  try {
138
232
  let delivered = true;
139
233
  let abort = null;
234
+ // Sampled inside the lock, immediately before the first byte (below), so the comparison
235
+ // spans exactly the paced write and not the lock wait that preceded it.
236
+ let inputSeqBefore = session.inputSeq;
237
+ // The origin is HARD-CODED here rather than forwarded through a parameter (Issue #1473).
238
+ // A one-arg function is assignable to a two-arg function type, so a wrapper that forgot to
239
+ // pass the origin along would compile cleanly — and its failure mode is the delivery
240
+ // counting its own bytes as human input, i.e. "mail never delivers". Removing the parameter
241
+ // removes the opportunity.
140
242
  const tracked = {
141
243
  write: (data) => {
142
- const ok = session.write(data);
244
+ const ok = session.write(data, 'delivery');
143
245
  if (!ok)
144
246
  delivered = false;
145
247
  return ok;
@@ -149,7 +251,8 @@ export async function submitMessagePaced(session, message, noEnter, precheck, cl
149
251
  abort = precheck();
150
252
  if (abort !== null)
151
253
  return 0; // refused in-lock: not one byte goes out
152
- return writeMessageToSession(tracked, message, noEnter);
254
+ inputSeqBefore = session.inputSeq;
255
+ return writeMessageToSession(tracked, message, noEnter, 0, strategy);
153
256
  }, clock);
154
257
  if (!ran)
155
258
  return { status: 'contended' };
@@ -162,6 +265,12 @@ export async function submitMessagePaced(session, message, noEnter, precheck, cl
162
265
  return { status: 'dropped' };
163
266
  if (bypasses.raced())
164
267
  return { status: 'preempted' };
268
+ // The prechecks close everything up to the first byte; between the first byte and the
269
+ // trailing Enter there is still an 80 ms-to-seconds window, and by then the bytes are out.
270
+ // So this is a REPORTING signal — flag it and let the caller tell the operator, rather than
271
+ // hold and re-write a message that may well have landed (#1584). Omitted when false.
272
+ if (session.inputSeq !== inputSeqBefore)
273
+ return { status: 'written', racedByInput: true };
165
274
  return { status: 'written' };
166
275
  }
167
276
  finally {
@@ -1 +1 @@
1
- {"version":3,"file":"message-write.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/message-write.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"message-write.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/message-write.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@
7
7
  * mode when the `gh` CLI is unavailable.
8
8
  */
9
9
  import type { ForgeIssueListItem } from '../../lib/github.js';
10
+ import { type RateLimitProbe } from '../../lib/forge-rate-limit.js';
10
11
  import type { PlanPhase, OverviewBuilder, OverviewBacklogItem, OverviewData } from '@cluesmith/codev-types';
11
12
  interface ParsedStatus {
12
13
  id: string;
@@ -57,6 +58,7 @@ export declare function calculateProgress(parsed: ParsedStatus, workspaceRoot?:
57
58
  * Each phase gets an equal share of 100%, with 'verified'/'complete' always = 100.
58
59
  */
59
60
  export declare function calculateEvenProgress(phase: string, phases: string[]): number;
61
+ /** Human-readable label for the gate the builder is blocked on, or null. */
60
62
  export declare function detectBlocked(parsed: ParsedStatus): string | null;
61
63
  /**
62
64
  * Canonical gate name (e.g. "plan-approval") for the gate the builder is
@@ -68,9 +70,9 @@ export declare function detectBlockedGate(parsed: ParsedStatus): string | null;
68
70
  * Detect when the current blocked gate was first requested.
69
71
  * Returns the ISO timestamp string or null if not blocked.
70
72
  *
71
- * Iterates `GATE_LABELS` so the gate set lives in exactly one place — sibling
72
- * to `detectBlocked` / `detectBlockedGate` (#927 removed this function's
73
- * previously-separate hardcoded array, which was a silent drift hazard).
73
+ * Shares `findBlockedGate` with `detectBlocked` / `detectBlockedGate` so the
74
+ * gate set lives in exactly one place (#927 removed a previously-separate
75
+ * hardcoded array, which was a silent drift hazard).
74
76
  */
75
77
  export declare function detectBlockedSince(parsed: ParsedStatus): string | null;
76
78
  /**
@@ -161,15 +163,28 @@ export declare function readFeedbackMode(workspaceRoot: string): 'forward' | 'qu
161
163
  * Derive backlog from open GitHub issues cross-referenced with specs and builders.
162
164
  */
163
165
  export declare function deriveBacklog(issues: ForgeIssueListItem[], workspaceRoot: string, activeBuilderIssues: Set<string>, prLinkedIssues: Set<string>): OverviewBacklogItem[];
166
+ export interface OverviewCacheOptions {
167
+ /** Clock, injectable for tests. */
168
+ now?: () => number;
169
+ /** Reset probe run once per fresh suspension (Tower passes the gh probe). */
170
+ probe?: RateLimitProbe;
171
+ }
164
172
  export declare class OverviewCache {
165
173
  private prCache;
166
174
  private issueCache;
167
175
  private closedCache;
168
176
  private mergedPRCache;
169
177
  private currentUserCache;
178
+ /** Single-flight per `<workspace>:<concept>` (#1645). */
179
+ private inflight;
180
+ /** Escalating failure window per `<workspace>:<budget>`. */
181
+ private failureWindows;
182
+ /** Epoch of the last invalidate(); open-list entries fetched before it are stale once debounced. */
183
+ private invalidatedAt;
184
+ private readonly limiter;
185
+ private readonly now;
186
+ constructor(options?: OverviewCacheOptions);
170
187
  private resolvedEnrichment;
171
- private readonly TTL;
172
- private readonly USER_TTL;
173
188
  /**
174
189
  * Build the overview response. Aggregates builder state, PRs, and backlog.
175
190
  *
@@ -180,20 +195,22 @@ export declare class OverviewCache {
180
195
  */
181
196
  getOverview(workspaceRoot: string, activeBuilderRoleIds?: Set<string>): Promise<OverviewData>;
182
197
  /**
183
- * Invalidate all cached data.
198
+ * Mark the open lists (PRs, issues) stale. Porch, cleanup and the VS Code
199
+ * review queue call this after every mutation, so it is not a human signal
200
+ * (#1645): it never clears a suspension or a cached failure, never touches
201
+ * the search or identity caches, and a list is refetched for it at most once
202
+ * per `INVALIDATE_DEBOUNCE_MS` per workspace.
184
203
  */
185
204
  invalidate(): void;
186
- private fetchPRsCached;
187
- private fetchIssuesCached;
188
205
  /**
189
- * Resolve the current user's forge login via the `user-identity` concept.
190
- * Long TTL — identity is stable for the lifetime of a Tower session.
191
- * Only successful resolutions are cached, so a transient failure (gh
192
- * logged out, offline) self-heals on the next overview poll.
206
+ * Serve `concept` for `cwd` from cache, or dispatch exactly one fetch.
207
+ * `listCache` entries additionally honour invalidate() (debounced).
193
208
  */
194
- private fetchCurrentUserCached;
195
- private fetchRecentlyClosedCached;
196
- private fetchMergedPRsCached;
209
+ private cachedFetch;
210
+ /** Run the concept once and record the outcome. Never rejects. */
211
+ private dispatch;
212
+ /** Current failure window for a key; escalates once per elapsed window, not per failure. */
213
+ private failureWindow;
197
214
  }
198
215
  export {};
199
216
  //# sourceMappingURL=overview.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"overview.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/servers/overview.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"overview.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/servers/overview.ts"],"names":[],"mappings":"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"}