@dalmasonto/taskflow-mcp 1.0.33 → 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.
- package/README.md +138 -193
- package/dist/attachment-download.d.ts +74 -0
- package/dist/attachment-download.js +193 -0
- package/dist/attachment-download.js.map +1 -0
- package/dist/attachments.d.ts +23 -0
- package/dist/attachments.js +66 -0
- package/dist/attachments.js.map +1 -0
- package/dist/client.d.ts +206 -0
- package/dist/client.js +279 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +137 -18
- package/dist/config.js +187 -106
- package/dist/config.js.map +1 -0
- package/dist/connect.d.ts +89 -0
- package/dist/connect.js +269 -0
- package/dist/connect.js.map +1 -0
- package/dist/doctor.d.ts +24 -0
- package/dist/doctor.js +120 -0
- package/dist/doctor.js.map +1 -0
- package/dist/events.d.ts +186 -0
- package/dist/events.js +407 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +124 -184
- package/dist/index.js.map +1 -0
- package/dist/instructions.d.ts +12 -0
- package/dist/instructions.js +114 -0
- package/dist/instructions.js.map +1 -0
- package/dist/mint.d.ts +62 -0
- package/dist/mint.js +135 -0
- package/dist/mint.js.map +1 -0
- package/dist/mirror.d.ts +68 -0
- package/dist/mirror.js +103 -0
- package/dist/mirror.js.map +1 -0
- package/dist/pane-queue.d.ts +29 -0
- package/dist/pane-queue.js +35 -0
- package/dist/pane-queue.js.map +1 -0
- package/dist/prompts.d.ts +79 -0
- package/dist/prompts.js +211 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resolve.d.ts +72 -0
- package/dist/resolve.js +89 -0
- package/dist/resolve.js.map +1 -0
- package/dist/runtime.d.ts +54 -0
- package/dist/runtime.js +321 -0
- package/dist/runtime.js.map +1 -0
- package/dist/server.d.ts +56 -0
- package/dist/server.js +793 -0
- package/dist/server.js.map +1 -0
- package/dist/session-identifier.d.ts +48 -0
- package/dist/session-identifier.js +44 -0
- package/dist/session-identifier.js.map +1 -0
- package/dist/sessions-store.d.ts +38 -0
- package/dist/sessions-store.js +88 -0
- package/dist/sessions-store.js.map +1 -0
- package/dist/tmux.d.ts +200 -0
- package/dist/tmux.js +580 -0
- package/dist/tmux.js.map +1 -0
- package/hooks/metadata.mjs +99 -0
- package/hooks/permission-prompt.mjs +100 -0
- package/hooks/taskflow-hook.mjs +499 -0
- package/hooks/tool-logging.mjs +63 -0
- package/package.json +38 -29
- package/dist/agent-registry.d.ts +0 -27
- package/dist/agent-registry.js +0 -153
- package/dist/db.d.ts +0 -5
- package/dist/db.js +0 -220
- package/dist/helpers.d.ts +0 -21
- package/dist/helpers.js +0 -27
- package/dist/resources.d.ts +0 -2
- package/dist/resources.js +0 -89
- package/dist/sse.d.ts +0 -10
- package/dist/sse.js +0 -766
- package/dist/tmux-bridge.d.ts +0 -12
- package/dist/tmux-bridge.js +0 -157
- package/dist/tools/activity.d.ts +0 -39
- package/dist/tools/activity.js +0 -152
- package/dist/tools/agent-inbox.d.ts +0 -12
- package/dist/tools/agent-inbox.js +0 -257
- package/dist/tools/agent.d.ts +0 -14
- package/dist/tools/agent.js +0 -168
- package/dist/tools/analytics.d.ts +0 -21
- package/dist/tools/analytics.js +0 -191
- package/dist/tools/checkpoint.d.ts +0 -27
- package/dist/tools/checkpoint.js +0 -105
- package/dist/tools/notifications.d.ts +0 -31
- package/dist/tools/notifications.js +0 -59
- package/dist/tools/projects.d.ts +0 -55
- package/dist/tools/projects.js +0 -112
- package/dist/tools/settings.d.ts +0 -19
- package/dist/tools/settings.js +0 -73
- package/dist/tools/tasks.d.ts +0 -105
- package/dist/tools/tasks.js +0 -403
- package/dist/tools/terminal.d.ts +0 -4
- package/dist/tools/terminal.js +0 -98
- package/dist/tools/timer.d.ts +0 -37
- package/dist/tools/timer.js +0 -154
- package/dist/types.d.ts +0 -82
- package/dist/types.js +0 -30
package/dist/runtime.js
ADDED
|
@@ -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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"}
|
package/dist/server.d.ts
ADDED
|
@@ -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;
|