@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
@@ -0,0 +1,321 @@
1
+ /**
2
+ * Everything that runs ONCE THERE IS A LIVE SESSION: the agent event stream
3
+ * (message delivery, prompt replay, terminal keys) and the tmux pane mirror.
4
+ *
5
+ * Extracted from `index.ts` because two callers need it — the bin entry at
6
+ * startup, and `select_profile` once a human has chosen an identity — and
7
+ * because a 170-line closure inside a bin entry is not a unit anyone can
8
+ * reason about.
9
+ *
10
+ * The pane is optional. Without one there is nothing to mirror and nowhere to
11
+ * type a message, so only the parts that need a pane are skipped; the session
12
+ * itself is already live and `check_messages` still works.
13
+ *
14
+ * It also holds the STARTUP entry (`startAgent`) and the two ways in — the bin
15
+ * entry at boot, and `select_profile` once a human has chosen an identity — so
16
+ * both take exactly the same path to a live agent.
17
+ */
18
+ import { formatIncoming, shouldDeliver, startAgentEventStream } from "./events.js";
19
+ import { detectTmuxPane, notifyPane, sendKeySteps, sendKeyToPane, startMirrorLoop, } from "./tmux.js";
20
+ import { createSerialQueue } from "./pane-queue.js";
21
+ import { stepsForPrompt } from "./prompts.js";
22
+ import { resolveMessage } from "./resolve.js";
23
+ import { findConfigPath, loadConfigFile, resolveProfileOrAsk, } from "./config.js";
24
+ import { readStickyProfile, writeStickyProfile } from "./sessions-store.js";
25
+ import { setNeedsProfile, startConnection } from "./connect.js";
26
+ import { reportMirror } from "./mirror.js";
27
+ const stderrLog = (line) => void process.stderr.write(`taskflow-mcp: ${line}\n`);
28
+ /**
29
+ * Adapt the client to {@link MessageSource}.
30
+ *
31
+ * `MessagesPage.messages` is `unknown[]` — the client deliberately does not
32
+ * model every row shape — so the narrowing happens here, once, at the boundary
33
+ * rather than inside the resolver.
34
+ */
35
+ function messageSourceFor(client) {
36
+ return {
37
+ listChannels: () => client.listChannels(),
38
+ listMessages: (params) => client.listMessages(params),
39
+ };
40
+ }
41
+ /**
42
+ * Attach the event stream (and, with a pane, the terminal mirror) to a live
43
+ * session.
44
+ *
45
+ * Returns a teardown that detaches BOTH. A profile switch must be able to call
46
+ * it: the mirror keeps calling `appendFrame(oldSession, …)` every couple of
47
+ * seconds and the backend reads a frame as proof of life, so an abandoned
48
+ * runtime keeps the OLD identity online in the dashboard however thoroughly its
49
+ * heartbeat was stopped — and its SSE stream keeps delivering that identity's
50
+ * DMs into this pane and marking them read as it. Idempotent.
51
+ */
52
+ export function startAgentRuntime(ctx, log) {
53
+ const { client, session, profile, pane } = ctx;
54
+ if (pane)
55
+ log(`mirroring tmux pane ${pane}`);
56
+ // Every write to the pane — message delivery, prompt replay, terminal
57
+ // keys — goes through this ONE serial queue. The event stream dispatches
58
+ // handlers fire-and-forget, so without serialization two writes that land
59
+ // together interleave their `send-keys <text>` / `send-keys Enter` steps
60
+ // and one message's Enter submits another's text (see pane-queue.ts).
61
+ const paneQueue = createSerialQueue();
62
+ // Deliver ONE message to the pane, resolving it by id (chat is id-only on
63
+ // the wire). Shared by the live stream and the reconnect catch-up so both
64
+ // apply the same shouldDeliver + notify + mark-read. The pane write itself
65
+ // is serialized; the resolve/markRead round-trips stay outside the lock so
66
+ // network latency doesn't stall other deliveries.
67
+ //
68
+ // Without a pane there is nowhere to type it, so we stop BEFORE markRead: the
69
+ // read cursor means "the agent has seen this", and nothing saw it.
70
+ // `check_messages` defaults to unread_only, so advancing the cursor here
71
+ // would drop the message for good (see planning/spec-message-delivery.md) —
72
+ // leaving it unread is what makes `check_messages` still work.
73
+ const deliverMessageById = async (id, edited = false) => {
74
+ const message = await resolveMessage(messageSourceFor(client), id);
75
+ if (!message)
76
+ return;
77
+ if (!shouldDeliver(message, profile.agentId))
78
+ return;
79
+ if (!pane)
80
+ return;
81
+ await paneQueue(() => notifyPane(formatIncoming(message, message.attachments ?? [], profile.agentId, edited), pane, true));
82
+ // For an edit of an already-read message this is a no-op — the cursor only
83
+ // ever moves forward — which is exactly right: redelivery is a notice, not
84
+ // new unread state.
85
+ await client.markRead(message.channel, message.id);
86
+ };
87
+ // On RE-connect, deliver anything that arrived while the stream was down.
88
+ // The live push is at-most-once and never redelivers, so a message sent
89
+ // during a backend/session restart would otherwise only surface via a
90
+ // manual check_messages — the "messages aren't coming" gap. Delivering
91
+ // unread (oldest first) closes it; already-delivered messages are past the
92
+ // read cursor, so they are not repeated.
93
+ const catchUpUnread = async () => {
94
+ // Nowhere to deliver, and `deliverMessageById` would drop every one of
95
+ // them anyway — so fetching the backlog is pure cost. `check_messages`
96
+ // still surfaces them on demand, which is the whole point of leaving
97
+ // them unread.
98
+ if (!pane)
99
+ return;
100
+ try {
101
+ const channels = await client.listChannels();
102
+ const ids = [];
103
+ for (const channel of channels) {
104
+ const page = await client.listMessages({ channel: channel.id, unread: true });
105
+ for (const row of page.messages) {
106
+ if (typeof row.id === "number")
107
+ ids.push(row.id);
108
+ }
109
+ }
110
+ ids.sort((a, b) => a - b);
111
+ if (ids.length) {
112
+ log(`reconnect catch-up — ${ids.length} missed message(s)`);
113
+ }
114
+ for (const id of ids) {
115
+ try {
116
+ await deliverMessageById(id);
117
+ }
118
+ catch (err) {
119
+ log(`catch-up could not deliver ${id} (${err.message.split("\n")[0]})`);
120
+ }
121
+ }
122
+ }
123
+ catch (err) {
124
+ log(`reconnect catch-up failed (${err.message.split("\n")[0]})`);
125
+ }
126
+ };
127
+ // Instant delivery: hold the event stream open and write each incoming
128
+ // message straight into the pane. Polling would make a message as slow to
129
+ // arrive as the capture interval, which is what "messages don't appear in
130
+ // the terminal" actually was.
131
+ const events = startAgentEventStream({
132
+ server: profile.server,
133
+ key: profile.key,
134
+ log,
135
+ // Deliver messages missed while the stream was down (see catchUpUnread).
136
+ onReconnect: catchUpUnread,
137
+ // A human answered a question the agent is blocked on: press the keys.
138
+ onPromptAnswered: async (prompt) => {
139
+ // No pane, no keyboard to replay the answer on.
140
+ if (!pane)
141
+ return;
142
+ // A prompt may carry SEVERAL questions, each with its own kind, and
143
+ // the terminal shows them one at a time. stepsForPrompt replays them
144
+ // in order and returns nothing at all for a half-answered set —
145
+ // leftover digits would land on whichever screen came next. A
146
+ // free-text "Other" answer becomes a text step typed into the field.
147
+ const steps = stepsForPrompt(prompt.options_json ?? "", prompt.kind, prompt.question, prompt.answer_json, prompt.answer, prompt.answer_text_json ?? null, prompt.status === "cancelled" ? "cancel" : "submit");
148
+ if (!steps.length)
149
+ return;
150
+ try {
151
+ // Paced, not a tight loop: Claude Code's multi-select drops
152
+ // keystrokes that arrive while it is re-rendering a toggle.
153
+ // Serialized with message delivery so an incoming message can't type
154
+ // itself into the middle of this answer's key sequence.
155
+ await paneQueue(() => sendKeySteps(steps, pane));
156
+ log(`answered prompt ${prompt.id} with ${steps.length} step(s)`);
157
+ }
158
+ catch (err) {
159
+ log(`could not answer prompt ${prompt.id} (${err.message.split("\n")[0]})`);
160
+ }
161
+ },
162
+ onMessage: async (event, action) => {
163
+ // The event carries the row id and NOTHING else — chat is id-only on
164
+ // the wire (its group is per-project while its rows are channel-
165
+ // scoped, see resolve.ts). deliverMessageById fetches the body/sender/
166
+ // attachments back over the authorized read API, applies shouldDeliver
167
+ // (a message this agent cannot see, or its own, is skipped), types it
168
+ // into the pane, and advances the read cursor. An "updated" action is
169
+ // an edit (#107), delivered with an EDITED framing so the agent
170
+ // continues from the revised content rather than treating it as new.
171
+ try {
172
+ await deliverMessageById(event.id, action === "updated");
173
+ }
174
+ catch (err) {
175
+ log(`could not deliver message ${event.id} (${err.message.split("\n")[0]})`);
176
+ }
177
+ },
178
+ // A human pressed a key in the dashboard terminal. The event is broadcast
179
+ // to the whole project, so act only on keys addressed to THIS agent's
180
+ // pane; send-keys types it as a key NAME (not the literal word).
181
+ onTerminalKey: async (input) => {
182
+ // No pane, nothing to send the key to.
183
+ if (!pane)
184
+ return;
185
+ if (input.agent !== profile.agentId)
186
+ return;
187
+ try {
188
+ await paneQueue(() => sendKeyToPane(input.keys, pane));
189
+ log(`terminal key "${input.keys}" → pane ${pane}`);
190
+ }
191
+ catch (err) {
192
+ log(`could not send key "${input.keys}" (${err.message.split("\n")[0]})`);
193
+ }
194
+ },
195
+ });
196
+ // TASKFLOW_MIRROR=off suppresses the MIRROR only — never the connection. The
197
+ // agent still registers, heartbeats and receives messages; it just doesn't
198
+ // stream its terminal. (Before Task 6 this flag gated the whole startup, so
199
+ // turning off the mirror also took the agent offline.)
200
+ let stopMirror = null;
201
+ if (pane && process.env.TASKFLOW_MIRROR !== "off") {
202
+ stopMirror = startMirrorLoop({
203
+ client,
204
+ session,
205
+ target: pane,
206
+ log,
207
+ // The mirror talks to the backend every couple of seconds, so it notices a
208
+ // restart long before the stream's idle watchdog would. Hand that over
209
+ // rather than making the stream wait for silence to accumulate.
210
+ onError: (err) => {
211
+ if (/network error|fetch failed|ECONNREFUSED/i.test(err.message)) {
212
+ events.reconnectNow();
213
+ }
214
+ },
215
+ });
216
+ // Publish what `whoami` reports. Since the mirror moved here, this module —
217
+ // not `startMirrorWithRetry` — is the only thing that knows the answer.
218
+ reportMirror({ state: "active", pane, attempts: 1 });
219
+ }
220
+ else {
221
+ reportMirror({
222
+ state: "off",
223
+ detail: pane ? "TASKFLOW_MIRROR=off" : "not running inside tmux — nothing to mirror",
224
+ attempts: 0,
225
+ });
226
+ }
227
+ let stopped = false;
228
+ return () => {
229
+ // Idempotent: the caller may tear down a runtime it already replaced, and
230
+ // stopping a mirror twice must not be an error worth thinking about.
231
+ if (stopped)
232
+ return;
233
+ stopped = true;
234
+ // Mirror first: it is what keeps a stale identity showing as online (the
235
+ // backend counts appended terminal frames as proof of life), and this
236
+ // teardown is idempotent-gated — if `events.stop()` threw first, the
237
+ // mirror would never stop and this function could not be retried.
238
+ stopMirror?.();
239
+ stopMirror = null;
240
+ events.stop();
241
+ };
242
+ }
243
+ /**
244
+ * Bring this agent online: resolve which identity this terminal is, then
245
+ * connect and start the runtime. When the repo defines several identities and
246
+ * nothing says which one this is, connect NOTHING and record `needs_profile` —
247
+ * guessing would silently collapse two terminals into one agent.
248
+ *
249
+ * Deliberately NOT conditional on tmux: registering a session is what makes the
250
+ * agent visible and reachable, and that must happen whether or not there is a
251
+ * pane to mirror.
252
+ */
253
+ export async function startAgent(options = {}) {
254
+ const configPath = options.configPath ?? findConfigPath();
255
+ const config = loadConfigFile(configPath);
256
+ const pane = await detectTmuxPane().catch(() => null);
257
+ const sticky = readStickyProfile({ configPath, pane });
258
+ const resolution = resolveProfileOrAsk(config, { env: process.env, configPath, sticky });
259
+ if (resolution.kind === "ambiguous") {
260
+ const names = resolution.profiles.map((p) => p.name).join(", ");
261
+ setNeedsProfile(`${resolution.profiles.length} profiles defined (${names}); waiting for select_profile`);
262
+ stderrLog(`multiple identities (${names}) — the agent must ask its human, then call select_profile`);
263
+ return;
264
+ }
265
+ connectAs(resolution.profile, pane);
266
+ }
267
+ /** The live connection, so a profile switch can stop the one it replaces. */
268
+ let current = null;
269
+ /** Its runtime (event stream + mirror), which must go down with it. */
270
+ let currentRuntime = null;
271
+ /**
272
+ * Which `connectAs` call owns the slots above.
273
+ *
274
+ * `onSession` fires whenever registration finally succeeds — which, with the
275
+ * backend down, can be minutes after a newer `connectAs` superseded this one.
276
+ * Without the token that late callback would overwrite the LIVE runtime's
277
+ * teardown with a stale one, and the next switch would tear down the wrong
278
+ * mirror while leaving the old one streaming. Same ownership rule as
279
+ * `connect.ts`'s status.
280
+ */
281
+ let runtimeOwner;
282
+ /**
283
+ * Connect as a known profile and start the runtime. Used by `select_profile`.
284
+ *
285
+ * Nothing is awaited: `startConnection`'s `settled` stays pending for as long as
286
+ * the backend is down, so awaiting it here would keep the MCP server from
287
+ * serving a single tool call until the backend came up.
288
+ */
289
+ export function connectAs(profile, pane) {
290
+ // A profile switch must not leave the old identity heartbeating: its session
291
+ // row would stay `connected` and the dashboard would show one agent twice.
292
+ // Stopping the heartbeat is not enough on its own — the old runtime's mirror
293
+ // keeps the row live by appending frames — so its runtime goes with it.
294
+ current?.stop();
295
+ currentRuntime?.();
296
+ currentRuntime = null;
297
+ const token = {};
298
+ runtimeOwner = token;
299
+ current = startConnection({
300
+ profile,
301
+ pane,
302
+ log: stderrLog,
303
+ onSession: (ctx) => {
304
+ const teardown = startAgentRuntime(ctx, stderrLog);
305
+ // Superseded while this connection was still registering: attach nothing
306
+ // and take it straight back down.
307
+ if (runtimeOwner !== token) {
308
+ teardown();
309
+ return;
310
+ }
311
+ currentRuntime = teardown;
312
+ },
313
+ });
314
+ }
315
+ /** Persist a human's pick for this terminal, then connect as it. */
316
+ export async function selectProfile(profile) {
317
+ const pane = await detectTmuxPane().catch(() => null);
318
+ writeStickyProfile(profile.profileName, { configPath: profile.configPath, pane });
319
+ connectAs(profile, pane);
320
+ }
321
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The TaskFlow MCP server (stdio transport).
3
+ *
4
+ * Loads `.taskflow.json` once, then registers one tool per meaningful agent
5
+ * operation. Each tool validates its arguments with zod, resolves a profile
6
+ * (per-call `profile` arg > `TASKFLOW_PROFILE` > `default_profile` > `main`),
7
+ * builds a {@link TaskflowClient} for that profile, calls the backend, and
8
+ * returns a concise text/JSON result. Errors are returned as tool errors
9
+ * (`isError: true`) with the backend's detail — never thrown out of the tool.
10
+ */
11
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
+ import { type ProfileChoice } from "./config.js";
13
+ import { type AgentSummary } from "./client.js";
14
+ /**
15
+ * The refusal returned instead of guessing an identity.
16
+ *
17
+ * The server cannot prompt a human — MCP has no such primitive — but it can
18
+ * return a machine-readable refusal naming the exact follow-up call that
19
+ * resolves it. This is the typed-error equivalent for a protocol with no
20
+ * interaction primitive.
21
+ */
22
+ export declare function ambiguityRefusal(profiles: ProfileChoice[]): string;
23
+ /** Whether a roster row describes an agent that is live right now. */
24
+ export declare function isLiveAgentStatus(status: string): boolean;
25
+ /**
26
+ * Annotate each choice with whether that agent already has a live session, so
27
+ * the human can see which identity another terminal has taken.
28
+ *
29
+ * `agents` is null when liveness could not be determined; `in_use` is then
30
+ * omitted rather than guessed — a picker with names only is still usable, but a
31
+ * wrong `in_use` would send the human to the wrong terminal.
32
+ */
33
+ export declare function markInUse(profiles: ProfileChoice[], agents: AgentSummary[] | null, agentIdByProfile: Record<string, number>): ProfileChoice[];
34
+ /**
35
+ * The warning returned when a human picks an identity another terminal already
36
+ * holds.
37
+ *
38
+ * Deliberately NOT a refusal. A crashed terminal's session still looks live for
39
+ * the rest of the 90s window, so refusing would lock someone out of their own
40
+ * identity at the worst possible moment. The collision is real but recoverable;
41
+ * being unable to reconnect is neither.
42
+ *
43
+ * Returns undefined when there is nothing to warn about, so the caller can
44
+ * spread it into the result and have the field simply not appear.
45
+ */
46
+ export declare function collisionWarning(profileName: string, live: AgentSummary | null): string | undefined;
47
+ export interface BuildServerOptions {
48
+ configPath?: string;
49
+ startDir?: string;
50
+ env?: NodeJS.ProcessEnv;
51
+ }
52
+ /**
53
+ * Construct the MCP server with all tools registered. The config is loaded
54
+ * eagerly so a broken `.taskflow.json` fails fast at startup with a clear error.
55
+ */
56
+ export declare function buildServer(options?: BuildServerOptions): McpServer;