@dalmasonto/taskflow-mcp 1.0.35 → 2.0.0

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 (101) hide show
  1. package/README.md +138 -193
  2. package/dist/attachment-download.d.ts +74 -0
  3. package/dist/attachment-download.js +193 -0
  4. package/dist/attachment-download.js.map +1 -0
  5. package/dist/attachments.d.ts +23 -0
  6. package/dist/attachments.js +66 -0
  7. package/dist/attachments.js.map +1 -0
  8. package/dist/client.d.ts +206 -0
  9. package/dist/client.js +279 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/config.d.ts +137 -18
  12. package/dist/config.js +187 -106
  13. package/dist/config.js.map +1 -0
  14. package/dist/connect.d.ts +89 -0
  15. package/dist/connect.js +269 -0
  16. package/dist/connect.js.map +1 -0
  17. package/dist/doctor.d.ts +24 -0
  18. package/dist/doctor.js +120 -0
  19. package/dist/doctor.js.map +1 -0
  20. package/dist/events.d.ts +186 -0
  21. package/dist/events.js +407 -0
  22. package/dist/events.js.map +1 -0
  23. package/dist/index.d.ts +7 -1
  24. package/dist/index.js +121 -218
  25. package/dist/index.js.map +1 -0
  26. package/dist/instructions.d.ts +12 -0
  27. package/dist/instructions.js +114 -0
  28. package/dist/instructions.js.map +1 -0
  29. package/dist/mint.d.ts +62 -0
  30. package/dist/mint.js +135 -0
  31. package/dist/mint.js.map +1 -0
  32. package/dist/mirror.d.ts +68 -0
  33. package/dist/mirror.js +103 -0
  34. package/dist/mirror.js.map +1 -0
  35. package/dist/pane-queue.d.ts +29 -0
  36. package/dist/pane-queue.js +35 -0
  37. package/dist/pane-queue.js.map +1 -0
  38. package/dist/prompts.d.ts +79 -0
  39. package/dist/prompts.js +211 -0
  40. package/dist/prompts.js.map +1 -0
  41. package/dist/resolve.d.ts +72 -0
  42. package/dist/resolve.js +89 -0
  43. package/dist/resolve.js.map +1 -0
  44. package/dist/runtime.d.ts +54 -0
  45. package/dist/runtime.js +321 -0
  46. package/dist/runtime.js.map +1 -0
  47. package/dist/server.d.ts +56 -0
  48. package/dist/server.js +793 -0
  49. package/dist/server.js.map +1 -0
  50. package/dist/session-identifier.d.ts +48 -0
  51. package/dist/session-identifier.js +44 -0
  52. package/dist/session-identifier.js.map +1 -0
  53. package/dist/sessions-store.d.ts +38 -0
  54. package/dist/sessions-store.js +88 -0
  55. package/dist/sessions-store.js.map +1 -0
  56. package/dist/tmux.d.ts +200 -0
  57. package/dist/tmux.js +580 -0
  58. package/dist/tmux.js.map +1 -0
  59. package/hooks/metadata.mjs +99 -0
  60. package/hooks/permission-prompt.mjs +100 -0
  61. package/hooks/taskflow-hook.mjs +499 -0
  62. package/hooks/tool-logging.mjs +63 -0
  63. package/package.json +38 -29
  64. package/dist/agent-registry.d.ts +0 -28
  65. package/dist/agent-registry.js +0 -158
  66. package/dist/db.d.ts +0 -5
  67. package/dist/db.js +0 -220
  68. package/dist/helpers.d.ts +0 -21
  69. package/dist/helpers.js +0 -27
  70. package/dist/resources.d.ts +0 -2
  71. package/dist/resources.js +0 -89
  72. package/dist/retry.d.ts +0 -34
  73. package/dist/retry.js +0 -94
  74. package/dist/sse.d.ts +0 -10
  75. package/dist/sse.js +0 -824
  76. package/dist/tmux-bridge.d.ts +0 -13
  77. package/dist/tmux-bridge.js +0 -217
  78. package/dist/tools/activity.d.ts +0 -39
  79. package/dist/tools/activity.js +0 -152
  80. package/dist/tools/agent-inbox.d.ts +0 -12
  81. package/dist/tools/agent-inbox.js +0 -272
  82. package/dist/tools/agent.d.ts +0 -14
  83. package/dist/tools/agent.js +0 -168
  84. package/dist/tools/analytics.d.ts +0 -21
  85. package/dist/tools/analytics.js +0 -191
  86. package/dist/tools/checkpoint.d.ts +0 -27
  87. package/dist/tools/checkpoint.js +0 -105
  88. package/dist/tools/notifications.d.ts +0 -31
  89. package/dist/tools/notifications.js +0 -59
  90. package/dist/tools/projects.d.ts +0 -55
  91. package/dist/tools/projects.js +0 -112
  92. package/dist/tools/settings.d.ts +0 -19
  93. package/dist/tools/settings.js +0 -73
  94. package/dist/tools/tasks.d.ts +0 -105
  95. package/dist/tools/tasks.js +0 -403
  96. package/dist/tools/terminal.d.ts +0 -4
  97. package/dist/tools/terminal.js +0 -98
  98. package/dist/tools/timer.d.ts +0 -37
  99. package/dist/tools/timer.js +0 -154
  100. package/dist/types.d.ts +0 -83
  101. package/dist/types.js +0 -30
package/dist/tmux.js ADDED
@@ -0,0 +1,580 @@
1
+ /**
2
+ * `taskflow-mcp --tmux [target]` — mirror a tmux pane into TaskFlow so humans
3
+ * can watch what an agent is doing.
4
+ *
5
+ * WHY A MIRROR AND NOT A LOG. The obvious design is to tail new output and append
6
+ * it as frames. That does not work for the case this exists for: a full-screen
7
+ * TUI agent (Claude Code itself) runs on tmux's ALTERNATE SCREEN buffer, which
8
+ * has no scrollback — `history_size` stays flat no matter how much scrolls by,
9
+ * so there is no stream of new lines to tail. The only observable state is the
10
+ * current screen, redrawn in place. So each capture is sent as a `snapshot`
11
+ * frame that REPLACES the view rather than appending to it.
12
+ *
13
+ * Sends only when the screen actually changed, so an idle agent costs one cheap
14
+ * capture per tick and no writes.
15
+ *
16
+ * By default the pane is only ever READ (`capture-pane`) — nothing is injected and
17
+ * no tmux option is altered, so mirroring cannot disturb the session. `--notify`
18
+ * opts in to WRITING: a one-line unread notice typed into the pane. See
19
+ * {@link buildNotice} for why that text is composed locally and never by the
20
+ * server.
21
+ */
22
+ import { execFile } from "node:child_process";
23
+ import { hostname } from "node:os";
24
+ import { promisify } from "node:util";
25
+ import { TaskflowClient } from "./client.js";
26
+ import { loadProfile } from "./config.js";
27
+ const run = promisify(execFile);
28
+ /**
29
+ * How often the pane is CAPTURED. This is a local `tmux capture-pane` — a few
30
+ * milliseconds, no network — so it can be frequent without costing anything.
31
+ * What it costs to send is governed separately, below.
32
+ */
33
+ export const DEFAULT_INTERVAL_MS = 1000;
34
+ /**
35
+ * Minimum gap between two frames on the wire.
36
+ *
37
+ * A working agent repaints constantly — spinners, elapsed-time counters, a
38
+ * cursor — so "the screen changed" is true almost every capture, and sending on
39
+ * every change would ship a full screen per tick forever. Changes are coalesced
40
+ * instead: at most one frame per second, and the LATEST capture wins, so the
41
+ * viewer sees current state rather than a queue of stale ones.
42
+ */
43
+ const MIN_SEND_INTERVAL_MS = 1000;
44
+ /**
45
+ * How often to prove liveness when the screen is NOT changing.
46
+ *
47
+ * Appending a frame already bumps the session's `last_seen_at` server-side, so a
48
+ * streaming agent never needs a heartbeat. Only a quiet one does, and the
49
+ * liveness window is 90s — beating every couple of seconds was chatter for its
50
+ * own sake.
51
+ */
52
+ const HEARTBEAT_INTERVAL_MS = 30_000;
53
+ /** Backend cap is 20k chars; stay under it so a wide pane can't be rejected. */
54
+ const MAX_SNAPSHOT_CHARS = 18_000;
55
+ /**
56
+ * How much of a message notice may be TYPED into the pane.
57
+ *
58
+ * Larger than the status-line cap because this text is the agent's only view of
59
+ * an incoming message: it must carry the whole body plus the attachment
60
+ * manifest, or the agent answers half a request without knowing. Still bounded —
61
+ * it becomes literal keystrokes in a live session.
62
+ */
63
+ const MAX_TYPED_NOTICE_CHARS = 1_200;
64
+ /**
65
+ * Build the notice typed into the agent's pane.
66
+ *
67
+ * The text is composed HERE, locally, from counts — never from anything the
68
+ * server sends. That is the whole security posture of this feature: the backend
69
+ * cannot dictate keystrokes into a live agent session, because it never supplies
70
+ * the string. Message bodies are deliberately excluded; the agent fetches them
71
+ * through `check_messages`, which is auditable, rather than having them typed
72
+ * into its prompt by a poller.
73
+ *
74
+ * Control characters are stripped and the result is a single line: a stray
75
+ * newline would submit the prompt early, and an escape sequence could rewrite
76
+ * the pane.
77
+ */
78
+ export function buildNotice(unread, channels) {
79
+ const where = channels > 1 ? `${channels} channels` : "a channel";
80
+ return sanitizeForPane(`[taskflow] ${unread} unread message${unread === 1 ? "" : "s"} in ${where} — ` +
81
+ `call check_messages, then mark_read when handled.`);
82
+ }
83
+ /** One line, printable characters only — see {@link buildNotice}. */
84
+ export function sanitizeForPane(text, max = 240) {
85
+ // eslint-disable-next-line no-control-regex
86
+ // Strip whole ANSI escape sequences first: dropping only the ESC byte
87
+ // would leave its printable tail ("[2J") as visible noise. Then remove any
88
+ // remaining C0/C1 controls — a newline would submit the prompt early, and a
89
+ // bare ESC reads as the Escape KEY to a TUI, which can cancel its input.
90
+ const flat = text
91
+ // eslint-disable-next-line no-control-regex
92
+ .replace(/\x1B\[[0-?]*[ -\/]*[@-~]/g, " ")
93
+ // eslint-disable-next-line no-control-regex
94
+ .replace(/[\x00-\x1F\x7F-\x9F]+/g, " ")
95
+ .replace(/\s+/g, " ")
96
+ .trim();
97
+ return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
98
+ }
99
+ /**
100
+ * Find the tmux pane this agent is running in, with NO configuration.
101
+ *
102
+ * The MCP server is spawned by the agent (Claude Code), so it can work out where
103
+ * that agent lives rather than being told:
104
+ *
105
+ * 1. `$TMUX_PANE` — set by tmux and inherited straight down. Exact and free.
106
+ * 2. Otherwise match the ANCESTOR's controlling terminal to a pane's tty
107
+ * (v1's method): `readlink /proc/<pid>/fd/0` gives the pty, and
108
+ * `tmux list-panes` maps ptys to panes. We walk a few generations up because
109
+ * the MCP server may sit behind a shell wrapper, and only the agent process
110
+ * itself is attached to the pane's tty.
111
+ *
112
+ * Returns null when not under tmux, which is a normal state, not an error.
113
+ */
114
+ export async function detectTmuxPane() {
115
+ const fromEnv = process.env.TMUX_PANE?.trim();
116
+ if (fromEnv)
117
+ return fromEnv;
118
+ let panes;
119
+ try {
120
+ const { stdout } = await run("tmux", ["list-panes", "-a", "-F", "#{pane_id} #{pane_tty}"]);
121
+ panes = stdout
122
+ .trim()
123
+ .split("\n")
124
+ .map((line) => {
125
+ const [id, tty] = line.split(" ");
126
+ return { id: id ?? "", tty: tty ?? "" };
127
+ })
128
+ .filter((p) => p.id && p.tty);
129
+ }
130
+ catch {
131
+ return null; // no tmux server
132
+ }
133
+ if (!panes.length)
134
+ return null;
135
+ let pid = process.ppid;
136
+ for (let depth = 0; depth < 4 && pid && pid > 1; depth += 1) {
137
+ try {
138
+ const { stdout } = await run("readlink", [`/proc/${pid}/fd/0`]);
139
+ const tty = stdout.trim();
140
+ const hit = panes.find((p) => p.tty === tty);
141
+ if (hit)
142
+ return hit.id;
143
+ }
144
+ catch {
145
+ /* pid gone, or no /proc (non-Linux) — try the next ancestor */
146
+ }
147
+ pid = await parentPid(pid);
148
+ }
149
+ return null;
150
+ }
151
+ /** The parent pid of `pid`, or undefined when it cannot be read. */
152
+ async function parentPid(pid) {
153
+ try {
154
+ const { stdout } = await run("ps", ["-o", "ppid=", "-p", String(pid)]);
155
+ const parsed = Number(stdout.trim());
156
+ return Number.isFinite(parsed) && parsed > 1 ? parsed : undefined;
157
+ }
158
+ catch {
159
+ return undefined;
160
+ }
161
+ }
162
+ /** Is tmux installed and is a server running? */
163
+ export async function tmuxAvailable() {
164
+ try {
165
+ await run("tmux", ["list-panes", "-a", "-F", "#{pane_id}"]);
166
+ return true;
167
+ }
168
+ catch {
169
+ return false;
170
+ }
171
+ }
172
+ /**
173
+ * Capture a pane's visible screen as plain text. Deliberately WITHOUT `-e`, so
174
+ * tmux resolves colour/cursor escapes into rendered text — the transcript stores
175
+ * readable content instead of raw ANSI the UI would have to interpret.
176
+ *
177
+ * Trailing blank lines are dropped: a TUI pads the screen to its full height, and
178
+ * keeping that padding would make every snapshot differ on cursor movement alone.
179
+ */
180
+ export async function capturePane(target) {
181
+ const args = ["capture-pane", "-p"];
182
+ if (target)
183
+ args.push("-t", target);
184
+ const { stdout } = await run("tmux", args, { maxBuffer: 4 * 1024 * 1024 });
185
+ return normalizeSnapshot(stdout);
186
+ }
187
+ /**
188
+ * Trim a raw capture into what gets stored: trailing blank lines dropped, and
189
+ * the TAIL kept when oversized (the bottom of a screen is where current output
190
+ * is; truncating the head loses scrolled-away context, truncating the tail would
191
+ * lose the live part).
192
+ */
193
+ export function normalizeSnapshot(raw, max = MAX_SNAPSHOT_CHARS) {
194
+ const trimmed = raw.replace(/\s+$/u, "");
195
+ return trimmed.length > max ? trimmed.slice(trimmed.length - max) : trimmed;
196
+ }
197
+ /**
198
+ * Deliver a notice to a pane. The two modes are deliberately different
199
+ * MECHANISMS, not the same one with a flag:
200
+ *
201
+ * - `submit: false` → `display-message`, which paints the status line and never
202
+ * touches the input buffer. Typing an unsubmitted notice instead (the obvious
203
+ * first attempt) corrupts whatever the human or agent was mid-way through
204
+ * typing, and successive notices concatenate into one unreadable line.
205
+ * - `submit: true` → `send-keys -l` + `Enter`, delivering the notice as input so
206
+ * the agent actually processes it. `-l` sends the text LITERALLY; without it
207
+ * tmux reads words like "Enter" as key names.
208
+ *
209
+ * Non-submit is for a human watching the pane; submit is for waking the agent.
210
+ */
211
+ /**
212
+ * Pause between typing a submitted notice and pressing Enter.
213
+ *
214
+ * #108: Codex's composer treats a CR that arrives in the same input burst as
215
+ * the preceding text as a PASTED newline, not a submit — so back-to-back
216
+ * `send-keys -l` + `send-keys Enter` left the notice sitting in the composer.
217
+ * Measured live against Codex 0.139: the immediate Enter is swallowed; the
218
+ * same Enter ≥50ms later submits every time. 150ms buys a 3× margin and is
219
+ * imperceptible next to the 1s mirror cadence. Claude Code tolerates the
220
+ * pause too, so it is unconditional rather than per-TUI.
221
+ */
222
+ export const SUBMIT_ENTER_DELAY_MS = 150;
223
+ /**
224
+ * #108 round two: for a LARGE notice (~1KB — a full message with attachment
225
+ * manifest), Codex collapses the burst into a "[Pasted Content N chars]" chip
226
+ * and CONSUMES the first Enter outright — no delay fixes it (measured on
227
+ * Codex 0.139: a single Enter still failed 2s after the text). What does work
228
+ * is pressing Enter AGAIN once the paste has settled. So after the first
229
+ * Enter, press it twice more on this schedule. The retries are no-ops when
230
+ * the first press already submitted: an empty composer ignores Enter, and a
231
+ * running turn does too (verified live with small and large payloads on
232
+ * Codex 0.139 and 0.146, and Claude Code behaves the same). Cost: ~2.7s per
233
+ * delivery inside the serial pane queue — chat cadence, not a hot path.
234
+ */
235
+ export const SUBMIT_ENTER_RETRIES_MS = [1000, 1500];
236
+ export async function notifyPane(text, target, submit = false, deps = {}) {
237
+ const exec = deps.exec ?? ((args) => run("tmux", args));
238
+ const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
239
+ const base = target ? ["-t", target] : [];
240
+ // The two modes have different limits because they are different surfaces.
241
+ // `display-message` paints tmux's status line — one row, so a long notice is
242
+ // unreadable there. `send-keys` TYPES into the pane, where the agent reads it
243
+ // as input, and cutting at 240 silently lost the tail of real messages: a
244
+ // request arrived half-written and was answered as if complete.
245
+ const line = sanitizeForPane(text, submit ? MAX_TYPED_NOTICE_CHARS : 240);
246
+ if (!submit) {
247
+ await exec(["display-message", ...base, line]);
248
+ return;
249
+ }
250
+ await exec(["send-keys", ...base, "-l", line]);
251
+ await sleep(SUBMIT_ENTER_DELAY_MS);
252
+ await exec(["send-keys", ...base, "Enter"]);
253
+ // Re-press Enter after the paste settles — see SUBMIT_ENTER_RETRIES_MS.
254
+ for (const delay of SUBMIT_ENTER_RETRIES_MS) {
255
+ await sleep(delay);
256
+ await exec(["send-keys", ...base, "Enter"]);
257
+ }
258
+ }
259
+ /**
260
+ * Send ONE key to a pane, by tmux key NAME (not literally).
261
+ *
262
+ * Answering a prompt means pressing keys, not typing text: "Right" must arrive
263
+ * as the arrow key that opens the review pane, and "1" as the digit that toggles
264
+ * an option. `send-keys -l` (used for message delivery) would type the literal
265
+ * word "Right", so this deliberately omits it.
266
+ */
267
+ export async function sendKeyToPane(key, target) {
268
+ const base = target ? ["-t", target] : [];
269
+ // Whitelist: only ever send a bare digit or a named navigation key. Without
270
+ // this, an answer travelling from the server could name any tmux key —
271
+ // "C-c" would kill the agent.
272
+ if (!/^(?:[0-9]|Right|Left|Up|Down|Enter|Space|Tab|Escape|BSpace)$/.test(key)) {
273
+ throw new Error(`refusing to send unrecognised key "${key}"`);
274
+ }
275
+ await run("tmux", ["send-keys", ...base, key]);
276
+ }
277
+ /**
278
+ * How long to wait between keystrokes in a replayed answer.
279
+ *
280
+ * Keys fired back to back sometimes dropped the middle ones: Claude Code's
281
+ * multi-select re-renders on each toggle, and a keystroke arriving mid-render
282
+ * was lost. Observed non-deterministically — [1,2,3,4] recorded once as {1,4}
283
+ * and once as {1,2,3,4} from identical input. A short pause lets the TUI settle
284
+ * between keys. Human-in-the-loop, so ~700ms for a four-option answer is fine.
285
+ */
286
+ const KEY_SEQUENCE_DELAY_MS = 90;
287
+ /**
288
+ * Send a sequence of keys to a pane with a pause BETWEEN each (never after the
289
+ * last), so a re-rendering TUI does not drop the ones in the middle.
290
+ */
291
+ export async function sendKeySequence(keys, target, deps = {}) {
292
+ const sendKey = deps.sendKey ?? sendKeyToPane;
293
+ const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
294
+ const delayMs = deps.delayMs ?? KEY_SEQUENCE_DELAY_MS;
295
+ for (let i = 0; i < keys.length; i++) {
296
+ if (i > 0)
297
+ await sleep(delayMs);
298
+ await sendKey(keys[i], target);
299
+ }
300
+ }
301
+ /** Type literal text into a pane (message-delivery style), newlines stripped. */
302
+ export async function typeTextToPane(text, target) {
303
+ const base = target ? ["-t", target] : [];
304
+ const clean = sanitizeForPane(text, 2000);
305
+ if (clean.length)
306
+ await run("tmux", ["send-keys", ...base, "-l", clean]);
307
+ }
308
+ /**
309
+ * How long to let a text field settle around a `{text}` step.
310
+ *
311
+ * A text input MOUNTS and FOCUSES when the caret navigates onto it — unlike a
312
+ * checkbox, which toggles instantly. A live test showed the plain 90ms inter-key
313
+ * gap fired the typed text before the field was focused, and the whole value was
314
+ * dropped. So a text step waits longer BEFORE (let the field focus) and AFTER
315
+ * (let the value register before the next key) than an ordinary key press.
316
+ */
317
+ const TEXT_SETTLE_MS = 400;
318
+ /** Send a mixed sequence of key presses and literal-text injections, paced. */
319
+ export async function sendKeySteps(steps, target, deps = {}) {
320
+ const sendKey = deps.sendKey ?? sendKeyToPane;
321
+ const typeText = deps.typeText ?? typeTextToPane;
322
+ const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
323
+ const delayMs = deps.delayMs ?? KEY_SEQUENCE_DELAY_MS;
324
+ for (let i = 0; i < steps.length; i++) {
325
+ const step = steps[i];
326
+ const prev = steps[i - 1];
327
+ // A text step needs the field focused first; the step AFTER a text step
328
+ // needs the typed value to have registered. Both gaps are longer than the
329
+ // ordinary inter-key pause.
330
+ if (i > 0) {
331
+ const brackets = "text" in step || (prev && "text" in prev);
332
+ await sleep(brackets ? TEXT_SETTLE_MS : delayMs);
333
+ }
334
+ if ("text" in step)
335
+ await typeText(step.text, target);
336
+ else
337
+ await sendKey(step.key, target);
338
+ }
339
+ }
340
+ /** Describe a pane for the session label, e.g. "0:0.0 (claude)". */
341
+ export async function describePane(target) {
342
+ const args = ["display-message", "-p"];
343
+ if (target)
344
+ args.push("-t", target);
345
+ args.push("#{session_name}:#{window_index}.#{pane_index} (#{pane_current_command})");
346
+ try {
347
+ const { stdout } = await run("tmux", args);
348
+ return stdout.trim();
349
+ }
350
+ catch {
351
+ return target ?? "tmux";
352
+ }
353
+ }
354
+ /**
355
+ * Stream a pane into an EXISTING session until stopped. Returns a stop function.
356
+ *
357
+ * Split out of the CLI path so the MCP server can run the same loop in-process:
358
+ * the mirror should not be a second thing a human remembers to launch.
359
+ */
360
+ export function startMirrorLoop(options) {
361
+ const { client, session, target } = options;
362
+ const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
363
+ const log = options.log ?? (() => { });
364
+ let lastSent = null;
365
+ let lastNotifiedMessageId = 0;
366
+ let stopped = false;
367
+ const notifyIfUnread = async () => {
368
+ const channels = await client.listChannels();
369
+ let unread = 0;
370
+ let channelsWithUnread = 0;
371
+ let highest = 0;
372
+ for (const channel of channels) {
373
+ const page = await client.listMessages({ channel: channel.id, unread: true });
374
+ const rows = page.messages;
375
+ if (!rows.length)
376
+ continue;
377
+ unread += rows.length;
378
+ channelsWithUnread += 1;
379
+ for (const row of rows)
380
+ if (typeof row.id === "number" && row.id > highest)
381
+ highest = row.id;
382
+ }
383
+ if (!unread || highest <= lastNotifiedMessageId)
384
+ return;
385
+ lastNotifiedMessageId = highest;
386
+ await notifyPane(buildNotice(unread, channelsWithUnread), target, options.notifySubmit);
387
+ log(` notified: ${unread} unread in ${channelsWithUnread} channel(s)`);
388
+ };
389
+ let lastSentAt = 0;
390
+ let lastContactAt = Date.now();
391
+ let inFlight = false;
392
+ const tick = async () => {
393
+ if (stopped || inFlight)
394
+ return;
395
+ try {
396
+ const current = await capturePane(target);
397
+ const now = Date.now();
398
+ const changed = current !== lastSent;
399
+ if (changed && now - lastSentAt >= MIN_SEND_INTERVAL_MS) {
400
+ inFlight = true;
401
+ try {
402
+ await client.appendFrame(session, { stream: "snapshot", content: current });
403
+ }
404
+ finally {
405
+ inFlight = false;
406
+ }
407
+ // Only mark it sent AFTER the write lands: a failed send must stay
408
+ // pending so the next tick retries rather than silently dropping it.
409
+ lastSent = current;
410
+ lastSentAt = now;
411
+ lastContactAt = now;
412
+ }
413
+ else if (!changed && now - lastContactAt >= HEARTBEAT_INTERVAL_MS) {
414
+ // Quiet screen: the only reason to touch the network is liveness.
415
+ await client.heartbeat(session, "busy");
416
+ lastContactAt = now;
417
+ }
418
+ // A change seen too soon after the last send is NOT dropped — `lastSent`
419
+ // is unchanged, so the next eligible tick sends the newest capture. That
420
+ // trailing send is what stops the view freezing on a stale screen when
421
+ // activity stops abruptly.
422
+ if (options.notify)
423
+ await notifyIfUnread();
424
+ }
425
+ catch (err) {
426
+ // Transient backend/tmux errors must not kill a long-running mirror.
427
+ inFlight = false;
428
+ log(` warn: ${err.message.split("\n")[0]}`);
429
+ options.onError?.(err);
430
+ }
431
+ };
432
+ const timer = setInterval(() => void tick(), intervalMs);
433
+ // Don't hold the process open on our account — the MCP transport decides
434
+ // lifetime, not this poller.
435
+ timer.unref?.();
436
+ void tick();
437
+ return () => {
438
+ stopped = true;
439
+ clearInterval(timer);
440
+ };
441
+ }
442
+ /**
443
+ * Auto-start mirroring for an agent that is running inside tmux. Silently does
444
+ * nothing when there is no pane to find — being outside tmux is normal.
445
+ */
446
+ export async function startAutoMirror(options) {
447
+ const pane = await detectTmuxPane();
448
+ if (!pane)
449
+ return null;
450
+ const log = options.log ?? (() => { });
451
+ log(`mirroring tmux pane ${pane} into the dashboard terminal`);
452
+ return startMirrorLoop({ ...options, target: pane });
453
+ }
454
+ /**
455
+ * Mirror the pane until interrupted. Resolves with an exit code.
456
+ */
457
+ export async function runTmuxMirror(options = {}) {
458
+ const log = options.log ?? ((l) => process.stdout.write(`${l}\n`));
459
+ const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
460
+ if (!(await tmuxAvailable())) {
461
+ log("FAIL tmux is not available (is a tmux server running?)");
462
+ return 1;
463
+ }
464
+ let resolved;
465
+ try {
466
+ resolved = options.resolved ?? loadProfile({ profile: options.profile });
467
+ }
468
+ catch (err) {
469
+ log(`FAIL ${err.message}`);
470
+ return 1;
471
+ }
472
+ // Fail fast on an unreachable pane rather than looping on errors.
473
+ let screen;
474
+ try {
475
+ screen = await capturePane(options.target);
476
+ }
477
+ catch (err) {
478
+ log(`FAIL could not capture pane${options.target ? ` "${options.target}"` : ""}.`);
479
+ log(` ${err.message.split("\n")[0]}`);
480
+ log(" List panes with: tmux list-panes -a");
481
+ return 1;
482
+ }
483
+ const label = await describePane(options.target);
484
+ const client = new TaskflowClient({ server: resolved.server, key: resolved.key });
485
+ let session;
486
+ try {
487
+ const row = await client.registerSession({
488
+ // Stable per pane, so re-running reconnects the same session instead of
489
+ // piling up rows the human has to pick between.
490
+ session_identifier: `tmux:${hostname()}:${label.split(" ")[0]}`,
491
+ host: hostname(),
492
+ pid: process.pid,
493
+ cwd: process.cwd(),
494
+ transport: "tmux",
495
+ });
496
+ session = row.id;
497
+ }
498
+ catch (err) {
499
+ log(`FAIL could not register a session: ${err.message}`);
500
+ return 1;
501
+ }
502
+ log(`Mirroring ${label} → ${resolved.server} as "${resolved.displayName}" (session ${session}).`);
503
+ log(`Sending a snapshot whenever the screen changes, every ${intervalMs}ms. Ctrl-C to stop.`);
504
+ if (options.notify) {
505
+ log(options.notifySubmit
506
+ ? "Notify: ON (submit) — unread notices are typed into the pane AND submitted, so the agent acts on them unprompted."
507
+ : "Notify: ON — unread notices show on the tmux status line, leaving the input untouched. Use --notify-submit to hand them to the agent instead.");
508
+ }
509
+ let lastSent = null;
510
+ let sent = 0;
511
+ let stopping = false;
512
+ const stop = async (signal) => {
513
+ if (stopping)
514
+ return;
515
+ stopping = true;
516
+ log(`\n${signal} — closing session ${session} (${sent} snapshot${sent === 1 ? "" : "s"} sent).`);
517
+ try {
518
+ await client.closeSession(session);
519
+ }
520
+ catch {
521
+ /* best effort: the session goes stale on its own within the heartbeat window */
522
+ }
523
+ process.exit(0);
524
+ };
525
+ process.on("SIGINT", () => void stop("SIGINT"));
526
+ process.on("SIGTERM", () => void stop("SIGTERM"));
527
+ // Notices are rate-limited to one per unread BURST: re-typing the same notice
528
+ // every tick would bury the pane and, with submit on, re-prompt the agent in a
529
+ // loop. A new notice only goes out when the highest unread id has moved, i.e.
530
+ // when something genuinely new arrived.
531
+ let lastNotifiedMessageId = 0;
532
+ /** Poll unread across channels; type one notice if there is anything new. */
533
+ const notifyIfUnread = async () => {
534
+ const channels = await client.listChannels();
535
+ let unread = 0;
536
+ let channelsWithUnread = 0;
537
+ let highest = 0;
538
+ for (const channel of channels) {
539
+ const page = await client.listMessages({ channel: channel.id, unread: true });
540
+ const rows = page.messages;
541
+ if (!rows.length)
542
+ continue;
543
+ unread += rows.length;
544
+ channelsWithUnread += 1;
545
+ for (const row of rows)
546
+ if (typeof row.id === "number" && row.id > highest)
547
+ highest = row.id;
548
+ }
549
+ if (!unread || highest <= lastNotifiedMessageId)
550
+ return;
551
+ lastNotifiedMessageId = highest;
552
+ await notifyPane(buildNotice(unread, channelsWithUnread), options.target, options.notifySubmit);
553
+ log(` notified: ${unread} unread in ${channelsWithUnread} channel(s)`);
554
+ };
555
+ // Heartbeat every tick so the agent reads as online for as long as we mirror;
556
+ // liveness is heartbeat-recency based, so a silent screen must still beat.
557
+ for (;;) {
558
+ if (stopping)
559
+ return 0;
560
+ try {
561
+ const current = await capturePane(options.target);
562
+ if (current !== lastSent) {
563
+ await client.appendFrame(session, { stream: "snapshot", content: current });
564
+ lastSent = current;
565
+ sent += 1;
566
+ }
567
+ else {
568
+ await client.heartbeat(session, "busy");
569
+ }
570
+ if (options.notify)
571
+ await notifyIfUnread();
572
+ }
573
+ catch (err) {
574
+ // Transient backend/tmux errors must not kill a long-running mirror.
575
+ log(` warn: ${err.message.split("\n")[0]}`);
576
+ }
577
+ await new Promise((r) => setTimeout(r, intervalMs));
578
+ }
579
+ }
580
+ //# sourceMappingURL=tmux.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux.js","sourceRoot":"","sources":["../src/tmux.ts"],"names":[],"mappings":"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"}