@dalmasonto/taskflow-mcp 1.0.35 → 2.1.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 +301 -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 +208 -0
  21. package/dist/events.js +454 -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 +339 -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/events.js ADDED
@@ -0,0 +1,454 @@
1
+ /**
2
+ * Listen to the agent's server-sent event stream so messages reach the terminal
3
+ * the instant they are sent.
4
+ *
5
+ * This is v1's tmux-bridge idea (`task_flow/mcp-server/src/tmux-bridge.ts`):
6
+ * hold a stream open for the life of the session and inject on arrival, instead
7
+ * of polling. Polling made delivery as slow as the interval and cost a request
8
+ * per tick even in silence.
9
+ *
10
+ * It cannot use `EventSource`: agents authenticate with `Authorization: Agent
11
+ * <key>`, and EventSource cannot set headers. Node's fetch can, so the SSE frame
12
+ * parsing is done here — it is a small, well-specified format.
13
+ */
14
+ /**
15
+ * Tiered reconnect backoff: retry FAST while a drop is probably a blip, then
16
+ * step the delay up the longer the server stays away — so a NAT64 socket drop
17
+ * recovers in seconds, while a real outage does not hammer a dead server. The
18
+ * argument is the count of CONSECUTIVE failures (reset to 0 on a live connect).
19
+ *
20
+ * attempts 1–10 → 5s (a transient drop: recover quickly)
21
+ * attempts 11–20 → 10s (still down: ease off)
22
+ * attempts 21+ → 20s (looks like an outage: slow, steady retries)
23
+ *
24
+ * Exported so the schedule is unit-tested directly rather than through timers.
25
+ */
26
+ export function reconnectDelayMs(consecutiveFailures) {
27
+ if (consecutiveFailures <= 10)
28
+ return 5_000;
29
+ if (consecutiveFailures <= 20)
30
+ return 10_000;
31
+ return 20_000;
32
+ }
33
+ /**
34
+ * After this many consecutive failed reconnects the server looks genuinely
35
+ * unreachable: tell the agent ONCE (so it stops waiting on a dead feed), then
36
+ * keep retrying quietly at the top tier so delivery self-heals when the server
37
+ * returns. The counter resets on a successful connect, so a later outage
38
+ * notifies again.
39
+ */
40
+ const UNREACHABLE_AFTER_ATTEMPTS = 30;
41
+ /**
42
+ * Treat the stream as dead after this long with NOTHING on it — not even a
43
+ * keep-alive comment.
44
+ *
45
+ * A dropped connection does not reliably surface as an error: when the server
46
+ * goes away mid-stream, `reader.read()` can stay pending forever, so the
47
+ * listener sits there believing it is connected while nothing is delivered.
48
+ * Waiting for an error is waiting for an event that never comes; the only sound
49
+ * signal is the absence of expected traffic. The server sends SSE keep-alives
50
+ * every ~15s, so ~25s of silence (one missed keep-alive plus slack) means the
51
+ * stream is gone. Kept tight on purpose: a stateful NAT64 gateway silently drops
52
+ * idle long-lived sockets, and the faster this trips the faster delivery
53
+ * recovers — a needless reconnect only costs a catch-up round trip.
54
+ */
55
+ const STREAM_IDLE_TIMEOUT_MS = 25_000;
56
+ /**
57
+ * Hard cap on the SSE read buffer (#44). Frames here are id-only and tiny; a
58
+ * buffer this large means the server is streaming a frame that never terminates
59
+ * with `\n\n`. Because bytes keep arriving, the idle watchdog never trips — so
60
+ * without this cap the buffer would grow to OOM. Past it we abort and reconnect.
61
+ */
62
+ const MAX_STREAM_BUFFER_BYTES = 1_048_576; // 1 MiB
63
+ /**
64
+ * Hold the stream open, reconnecting with backoff.
65
+ *
66
+ * Never rejects: a stream that cannot connect retries quietly forever, because
67
+ * the agent must keep working whether or not the dashboard is reachable.
68
+ */
69
+ export function startAgentEventStream(options) {
70
+ const log = options.log ?? (() => { });
71
+ const doFetch = options.fetchImpl ?? fetch;
72
+ const url = `${options.server.replace(/\/+$/, "")}/api/taskflow/agents/events`;
73
+ let stopped = false;
74
+ // Count of CONSECUTIVE failed (re)connects; 0 while connected.
75
+ let attempt = 0;
76
+ // Whether the agent has already been told the server is unreachable this spell
77
+ // — so the notice fires once per outage, not once per retry.
78
+ let unreachableNotified = false;
79
+ let everConnected = false;
80
+ let controller = null;
81
+ let retryTimer = null;
82
+ const scheduleRetry = () => {
83
+ if (stopped)
84
+ return;
85
+ attempt += 1;
86
+ // A sustained outage: tell the agent once its feed is dead, then keep
87
+ // retrying at the top tier so it recovers on its own when the server returns.
88
+ if (attempt >= UNREACHABLE_AFTER_ATTEMPTS && !unreachableNotified) {
89
+ unreachableNotified = true;
90
+ log(`event stream unreachable after ${attempt} attempts — notifying agent`);
91
+ void options.onUnreachable?.(attempt);
92
+ }
93
+ const delay = reconnectDelayMs(attempt);
94
+ retryTimer = setTimeout(connect, delay);
95
+ retryTimer.unref?.();
96
+ };
97
+ const connect = async () => {
98
+ if (stopped)
99
+ return;
100
+ controller = new AbortController();
101
+ try {
102
+ const res = await doFetch(url, {
103
+ headers: { Authorization: `Agent ${options.key}`, Accept: "text/event-stream" },
104
+ signal: controller.signal,
105
+ });
106
+ if (!res.ok || !res.body) {
107
+ scheduleRetry();
108
+ return;
109
+ }
110
+ attempt = 0;
111
+ log("event stream connected");
112
+ // Recovered from a sustained outage the agent was told about: say so, so it
113
+ // is not left believing the feed is still dead. Ordinary reconnects skip
114
+ // this — only the catch-up below runs for those.
115
+ if (unreachableNotified) {
116
+ unreachableNotified = false;
117
+ void options.onRecovered?.();
118
+ }
119
+ // A RE-connect may have missed messages while it was down; let the caller
120
+ // catch up. The first connect needs no catch-up — nothing was missed yet.
121
+ if (everConnected)
122
+ void options.onReconnect?.();
123
+ everConnected = true;
124
+ // Parse SSE frames. `data:` may be split across TCP chunks, so text is
125
+ // buffered until a blank line terminates the frame.
126
+ const reader = res.body.getReader();
127
+ const decoder = new TextDecoder();
128
+ let buffer = "";
129
+ // Watchdog: abort a stream that has gone quiet, which forces the read to
130
+ // settle and drops us into the reconnect path. Created AFTER getReader()
131
+ // (which can throw) and cleared in the finally below, so a throw between
132
+ // connect and the loop can never leak a watchdog — a leaked one would
133
+ // close over the reassigned `controller` and abort the NEXT live
134
+ // connection every ~15s (#44).
135
+ let lastActivity = Date.now();
136
+ const watchdog = setInterval(() => {
137
+ if (Date.now() - lastActivity > STREAM_IDLE_TIMEOUT_MS) {
138
+ log("event stream idle — assuming it died, reconnecting");
139
+ controller?.abort();
140
+ }
141
+ }, Math.floor(STREAM_IDLE_TIMEOUT_MS / 3));
142
+ watchdog.unref?.();
143
+ try {
144
+ for (;;) {
145
+ const { done, value } = await reader.read();
146
+ if (done)
147
+ break;
148
+ // Any byte counts as life, keep-alive comments included.
149
+ lastActivity = Date.now();
150
+ buffer += decoder.decode(value, { stream: true });
151
+ let boundary = buffer.indexOf("\n\n");
152
+ while (boundary !== -1) {
153
+ const frame = buffer.slice(0, boundary);
154
+ buffer = buffer.slice(boundary + 2);
155
+ handleFrame(frame, options);
156
+ boundary = buffer.indexOf("\n\n");
157
+ }
158
+ // A frame that never terminates would grow `buffer` unbounded while
159
+ // bytes keep arriving (so the watchdog never trips) — cap it and force a
160
+ // reconnect instead of growing to OOM (#44).
161
+ if (buffer.length > MAX_STREAM_BUFFER_BYTES) {
162
+ log("event stream buffer exceeded cap without a frame terminator — reconnecting");
163
+ controller?.abort();
164
+ break;
165
+ }
166
+ }
167
+ }
168
+ finally {
169
+ clearInterval(watchdog);
170
+ }
171
+ if (!stopped) {
172
+ log("event stream closed by server");
173
+ scheduleRetry();
174
+ }
175
+ }
176
+ catch (err) {
177
+ if (!stopped) {
178
+ log(`event stream error: ${err.message.split("\n")[0]}`);
179
+ scheduleRetry();
180
+ }
181
+ }
182
+ };
183
+ void connect();
184
+ return {
185
+ stop: () => {
186
+ stopped = true;
187
+ if (retryTimer)
188
+ clearTimeout(retryTimer);
189
+ controller?.abort();
190
+ },
191
+ reconnectNow: () => {
192
+ if (stopped)
193
+ return;
194
+ // Abort settles the pending read, which lands in the reconnect path.
195
+ // Reset the backoff: this is a known-cause drop, not a flapping server.
196
+ // The unreachable flag stays as-is: if we were in an outage, the next
197
+ // successful connect clears it and fires onRecovered; if this abort fails
198
+ // too, scheduleRetry keeps counting from 0 without re-notifying yet.
199
+ attempt = 0;
200
+ controller?.abort();
201
+ },
202
+ };
203
+ }
204
+ /** Pull the `data:` lines out of one SSE frame and dispatch a message event. */
205
+ export function handleFrame(frame, options) {
206
+ const data = frame
207
+ .split("\n")
208
+ .filter((line) => line.startsWith("data:"))
209
+ .map((line) => line.slice(5).trimStart())
210
+ .join("\n");
211
+ if (!data)
212
+ return;
213
+ let envelope;
214
+ try {
215
+ envelope = JSON.parse(data);
216
+ }
217
+ catch {
218
+ return;
219
+ }
220
+ // A prompt being ANSWERED is an update, not a creation — the row was created
221
+ // when the question appeared. So prompts are routed before the created-only
222
+ // filter below, or every answer would be dropped.
223
+ if (envelope.c?.endsWith(":prompts")) {
224
+ if (isAnsweredPrompt(envelope.d))
225
+ void options.onPromptAnswered?.(envelope.d);
226
+ return;
227
+ }
228
+ // Terminal keys: fields are projected inline, so act straight off the event.
229
+ // Broadcast to the whole project — the handler filters by `agent`.
230
+ if (envelope.c?.endsWith(":terminal_inputs")) {
231
+ const row = envelope.d;
232
+ if (row && typeof row.agent === "number" && typeof row.keys === "string") {
233
+ void options.onTerminalKey?.({ agent: row.agent, keys: row.keys });
234
+ }
235
+ return;
236
+ }
237
+ // Creations AND edits matter for delivery (#107): an edited message is the
238
+ // sender saying "proceed with THIS version", so it must reach the pane again.
239
+ // Deletes stay silent — there is nothing new for the agent to read.
240
+ if (envelope.e !== "created" && envelope.e !== "updated")
241
+ return;
242
+ // Every table in a project shares the `project:{id}:*` room family, and a chat
243
+ // event is now id-only — indistinguishable from an id-only task or read-cursor
244
+ // row except by the group name. Without this check every task edit would be
245
+ // typed into the agent's pane as though someone had spoken to it.
246
+ if (!envelope.c?.endsWith(":messages"))
247
+ return;
248
+ const row = envelope.d;
249
+ // The id is all the wire carries, and all that is needed: the body is fetched
250
+ // back over the authorized read API, which re-checks the channel roster.
251
+ // Requiring `body_markdown` here is what silently killed live delivery when
252
+ // chat became id-only — the guard rejected every real event.
253
+ if (!row || typeof row.id !== "number")
254
+ return;
255
+ void options.onMessage(row, envelope.e);
256
+ }
257
+ /**
258
+ * Whether a message should be delivered to THIS agent's terminal.
259
+ *
260
+ * Every message the agent can see is delivered EXCEPT its own — an agent must
261
+ * stay aware of all channel traffic so it can chat / add information (#40 review).
262
+ * Whether it may ACT on the message is a SEPARATE signal (`canActOnMessage`)
263
+ * surfaced as guidance in the notice, NOT a delivery gate. Its own messages are
264
+ * still skipped: echoing them back reads as a new instruction and could loop the
265
+ * agent against itself.
266
+ */
267
+ export function shouldDeliver(message, selfAgentId) {
268
+ if (message.sender_kind === "agent" && message.sender_agent === selfAgentId)
269
+ return false;
270
+ return true;
271
+ }
272
+ /**
273
+ * Whether THIS agent is a TARGET of the message and may act on it — build,
274
+ * execute, take ownership. A non-target still RECEIVES the message (to reply or
275
+ * add information) but must not act; `formatIncoming` says so.
276
+ *
277
+ * - An empty/absent target set is a broadcast: everyone may act.
278
+ * - Otherwise the agent may act only if it is one of the AGENT targets. A
279
+ * message tagged only at users leaves every agent a non-target.
280
+ */
281
+ export function canActOnMessage(message, selfAgentId) {
282
+ if (message.targets && message.targets.length > 0) {
283
+ return message.targets.some((t) => t.kind === "agent" && t.id === selfAgentId);
284
+ }
285
+ // Back-compat: rows written before `targets` carry only the single
286
+ // `target_agent`. Null there is a broadcast.
287
+ if (message.target_agent != null)
288
+ return message.target_agent === selfAgentId;
289
+ return true;
290
+ }
291
+ /**
292
+ * How much of a notice may be typed into the pane.
293
+ *
294
+ * The notice is delivered by TYPING it into a live tmux pane, so it has to stay
295
+ * bounded — but the old 240-char cap silently ate the tail of any real message.
296
+ * A request lost its second half mid-sentence and the agent answered the half it
297
+ * got, with nothing indicating more existed.
298
+ */
299
+ const MAX_NOTICE_CHARS = 1_000;
300
+ /** Bytes as something a human (or an agent deciding whether to read it) can judge. */
301
+ function humanSize(bytes) {
302
+ if (bytes < 1024)
303
+ return `${bytes} B`;
304
+ if (bytes < 1024 * 1024)
305
+ return `${Math.round(bytes / 1024)} KB`;
306
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
307
+ }
308
+ /**
309
+ * The one-line notice typed into the agent's pane for an incoming message.
310
+ *
311
+ * Attachments are listed explicitly. A pushed message used to carry body text
312
+ * only, so a file arrived invisibly: the agent read the message, answered it,
313
+ * and never learned anything was attached. Name, size and url are all included
314
+ * so the agent can decide whether to pull the file without another round trip.
315
+ *
316
+ * The manifest is built FIRST and the body flexes into whatever room is left.
317
+ * If something has to be dropped it must be prose, never the pointer to the
318
+ * files — the manifest is precisely what tells the agent there is more to fetch.
319
+ */
320
+ /**
321
+ * The context block appended to a notice: the ids an agent needs to act on the
322
+ * message without a round trip — scope a query to the project, act on the task,
323
+ * reply to the right person, or `mark_read` the message. Gated on `project`
324
+ * (a real delivered message always has one; the bare wire event does not), and
325
+ * kept on ONE line — a newline would submit the prompt early. Like the
326
+ * attachment manifest, it is never the part that gets truncated.
327
+ */
328
+ function contextSuffix(message) {
329
+ if (message.project == null)
330
+ return "";
331
+ const parts = [
332
+ `project=${message.project}`,
333
+ `channel=${message.channel}`,
334
+ `message=${message.id}`,
335
+ ];
336
+ if (message.task != null)
337
+ parts.push(`task=${message.task}`);
338
+ const sender = message.sender_user != null
339
+ ? `user:${message.sender_user}`
340
+ : message.sender_agent != null
341
+ ? `agent:${message.sender_agent}`
342
+ : null;
343
+ if (sender)
344
+ parts.push(`from=${sender}`);
345
+ return ` ⟦ctx ${parts.join(" ")}⟧`;
346
+ }
347
+ export function formatIncoming(message, attachments = [], selfAgentId, edited = false) {
348
+ const who = message.sender_label || (message.sender_kind === "user" ? "User" : "Agent");
349
+ // #40 review: a non-target still receives the message but is told, clearly and
350
+ // up front, NOT to act on it — only to reply / add information if useful. When
351
+ // selfAgentId is unknown, or the agent is a target/broadcast recipient, there
352
+ // is no guard and the message reads normally.
353
+ const guard = selfAgentId != null && !canActOnMessage(message, selfAgentId)
354
+ ? "🚫 DO NOT ACT — you are not the target of this message. You may reply or add information in chat, but do not build or execute on it. "
355
+ : "";
356
+ // #107: an edit is redelivered so the agent proceeds from the REVISED words —
357
+ // said explicitly, or the agent reads it as a brand-new (duplicate) request.
358
+ const head = edited
359
+ ? `[taskflow] ${guard}✏️ EDITED message from ${who} (supersedes the earlier version — continue from this revision): `
360
+ : `[taskflow] ${guard}Message from ${who}: `;
361
+ const manifest = attachments.length
362
+ ? ` 📎 ${attachments.length} attachment${attachments.length === 1 ? "" : "s"}: ` +
363
+ attachments.map((a) => `${a.name} (${humanSize(a.size_bytes)}) ${a.url}`).join("; ")
364
+ : "";
365
+ const ctx = contextSuffix(message);
366
+ // Newlines would submit the prompt early; collapse before measuring so the
367
+ // budget reflects what is actually typed.
368
+ const body = message.body_markdown.replace(/\s+/gu, " ").trim();
369
+ // The manifest AND the context block are pointers the agent must keep; only
370
+ // the prose flexes into the room left after them.
371
+ const room = MAX_NOTICE_CHARS - head.length - manifest.length - ctx.length;
372
+ if (body.length <= room)
373
+ return `${head}${body}${manifest}${ctx}`;
374
+ // Say so. A truncated notice that looks complete is how a request gets
375
+ // half-answered; the agent needs to know to go and read the rest.
376
+ const hint = " …[truncated — call check_messages for the full message]";
377
+ const kept = body.slice(0, Math.max(0, room - hint.length));
378
+ return `${head}${kept}${hint}${manifest}${ctx}`;
379
+ }
380
+ /**
381
+ * The keystrokes that answer a prompt, as verified against a live Claude Code
382
+ * session — not inferred from the UI's appearance.
383
+ *
384
+ * SINGLE-select: the number HIGHLIGHTS the option; Enter submits it.
385
+ * press "2" -> option 2 highlighted, still waiting
386
+ * press Enter -> "You selected: Green"
387
+ *
388
+ * This one was wrong here until 2026-07-21. The comment claimed the number both
389
+ * selected and submitted, so the agent was left sitting on a prompt the
390
+ * dashboard already showed as answered — the worst shape of failure, because
391
+ * the human sees a completed action and the agent sees nothing.
392
+ *
393
+ * MULTI-select: numbers TOGGLE checkboxes, then Right opens the review screen
394
+ * ("Ready to submit your answers?" — 1. Submit / 2. Cancel). This function only
395
+ * REACHES that screen; the submit key is appended once by keystrokesForPrompt,
396
+ * because a multi-question set lands on the identical screen. Verified live
397
+ * 2026-07-21: a single multi-select question sat on that review screen because
398
+ * nothing pressed the final key.
399
+ * press "1" -> [✔] Lint
400
+ * press "3" -> [✔] Tests
401
+ * press Right -> review screen (submit handled by the caller)
402
+ *
403
+ * Getting this wrong is not a cosmetic bug: sending a single digit to a
404
+ * multi-select ticks one box and leaves the agent still waiting, and sending
405
+ * Enter instead of Right submits an empty answer.
406
+ */
407
+ export function answerKeystrokes(choices, kind) {
408
+ if (!choices.length)
409
+ return [];
410
+ if (kind !== "multi")
411
+ return [String(choices[0]), "Enter"];
412
+ // Toggle each, then Right to open the review screen. The submit at that screen
413
+ // is NOT here — it is shared with multi-question sets and owned by
414
+ // keystrokesForPrompt. A trailing "1" here (as it was) highlighted submit
415
+ // without an Enter and left the agent waiting, the same bug single-select had.
416
+ return [...choices.map(String), "Right"];
417
+ }
418
+ /** The numbers a human chose, from whichever field carries them. */
419
+ export function chosenNumbers(prompt) {
420
+ if (prompt.answer_json) {
421
+ try {
422
+ const parsed = JSON.parse(prompt.answer_json);
423
+ if (Array.isArray(parsed))
424
+ return parsed.filter((n) => typeof n === "number");
425
+ }
426
+ catch {
427
+ /* fall through to the single value */
428
+ }
429
+ }
430
+ return prompt.answer != null ? [prompt.answer] : [];
431
+ }
432
+ /**
433
+ * Whether this event is a human decision this agent should act on.
434
+ *
435
+ * `answered` and `cancelled` are BOTH actionable: cancel is the other button on
436
+ * the review screen, and reaching it means replaying every answer first, so it
437
+ * is just as much a keystroke sequence as submit.
438
+ *
439
+ * `answered_by` is what separates a human cancel from the agent clearing its own
440
+ * prompt — it times out, or someone answers in the terminal directly, and the
441
+ * row is marked cancelled with nobody attributed. Firing keys for that would
442
+ * type digits at a screen the agent has already left.
443
+ */
444
+ export function isAnsweredPrompt(row) {
445
+ const prompt = row;
446
+ if (!prompt || typeof prompt.id !== "number")
447
+ return false;
448
+ if (prompt.answer == null && !prompt.answer_json)
449
+ return false;
450
+ if (prompt.status === "answered")
451
+ return true;
452
+ return prompt.status === "cancelled" && prompt.answered_by != null;
453
+ }
454
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import 'dotenv/config';
2
+ /**
3
+ * Bin entry: load `.taskflow.json`, build the MCP server, and serve over stdio.
4
+ *
5
+ * A configuration failure prints a clear message to stderr and exits non-zero so
6
+ * the human fixes the credential file rather than getting a silent dead server.
7
+ */
8
+ export {};