flow-agent-bridge 0.2.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 ADDED
@@ -0,0 +1,89 @@
1
+ # flow-agent-bridge
2
+
3
+ Run an AI coding agent (Claude Code, Codex, or any "prompt in, text out" CLI)
4
+ as a **first-class member of a [Flow](https://app.flowtoo.org) workspace** —
5
+ real presence, DMs, @-mentions, threads, file attachments, and live
6
+ "thinking…" progress while it works.
7
+
8
+ ```
9
+ Flow WS ──> flow-agent-bridge daemon ──spawn──> claude -p --resume <session>
10
+ ^ │ stream-json
11
+ └────── replies / reactions / uploads <────────────┘
12
+ ```
13
+
14
+ ## Install & run
15
+
16
+ ```sh
17
+ npm install -g flow-agent-bridge
18
+ flow-agent-bridge # interactive setup on first run, daemon after
19
+ ```
20
+
21
+ The first run walks you through everything: paste the server URL + one-time
22
+ invite key (a workspace admin mints it via **Invite an Agent…**), the key is
23
+ exchanged for a permanent agent token, and the config is saved to
24
+ `agent.json` (chmod 600) next to where you ran it. Subsequent runs just
25
+ start the daemon. Already have an agent? Paste a regenerated
26
+ `flow-agent-token-…` instead of an invite key to reconnect as it.
27
+
28
+ ## What you get
29
+
30
+ - **One CLI session per conversation** — each DM or thread maps to a
31
+ persistent `--session-id`/`--resume` session; separate conversations run
32
+ concurrently, turns within one are serialized. Send `/reset` to start a
33
+ conversation fresh.
34
+ - **Thinking steps** — tool calls stream into one status message that edits
35
+ in place while the agent works, then the final reply posts clean.
36
+ - **Attachments both ways** — incoming images/files are downloaded locally
37
+ and offered to the agent (Claude renders images natively); the agent can
38
+ send files back via the bundled `flow` MCP server (`send_message`,
39
+ `react`, `upload_file`, `search_history`).
40
+ - **cwd is the identity** — point the agent at a repo checkout and
41
+ "@RepoBot fix the failing test" runs the CLI in that repo.
42
+ - **Safety rails** — pre-scoped tool permissions (read-only by default),
43
+ sender gating, self/agent loop guards, `--max-turns` + wall-clock caps.
44
+
45
+ ## Config
46
+
47
+ `agent.json` (created by the wizard; edit freely, restart to apply):
48
+
49
+ ```json
50
+ {
51
+ "serverUrl": "https://app.flowtoo.org",
52
+ "agentToken": "flow-agent-token-…",
53
+ "runtime": {
54
+ "kind": "claude",
55
+ "cwd": "~/checkouts/repo-x",
56
+ "permissionMode": "acceptEdits",
57
+ "allowedTools": ["Read", "Grep", "Glob", "Bash(pnpm test:*)"]
58
+ },
59
+ "eventScope": "mentions",
60
+ "progress": "thinking",
61
+ "concurrency": 4
62
+ }
63
+ ```
64
+
65
+ | Key | Default | Meaning |
66
+ |---|---|---|
67
+ | `runtime.kind` | `claude` | `claude`, `codex` (stub), or `demo` (canned reply — wiring check) |
68
+ | `runtime.cwd` | config dir | working directory the CLI runs in (`~` expands) |
69
+ | `runtime.allowedTools` | `[]` | pre-granted permissions — headless runs can't prompt |
70
+ | `runtime.maxTurns` / `timeoutSec` | 25 / 300 | runaway caps |
71
+ | `eventScope` | `mentions` | `mentions` (@-mentions + DMs) or `all` channel traffic |
72
+ | `progress` | `thinking` | `thinking` \| `typing` \| `silent` |
73
+
74
+ Headless runtimes authenticate however the CLI normally does (e.g.
75
+ `claude setup-token` or `ANTHROPIC_API_KEY` in the daemon's environment).
76
+
77
+ ## Keep it running
78
+
79
+ The daemon only dials out (HTTPS + WSS) — no open ports needed. Under
80
+ systemd:
81
+
82
+ ```ini
83
+ [Service]
84
+ ExecStart=/usr/bin/env flow-agent-bridge /home/me/mybot.json
85
+ Restart=always
86
+ ```
87
+
88
+ It reconnects through server restarts on its own; run the wizard once
89
+ interactively before enabling the unit.
package/dist/api.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import type { AgentRegisterResponse, ChannelDTO, FileDTO, MessageDTO, MessagePage, UserDTO, WorkspaceDTO, WorkspaceMemberDTO } from '@flow/shared';
2
+ export declare class FlowApiError extends Error {
3
+ readonly status: number;
4
+ readonly code: string;
5
+ constructor(status: number, code: string, message: string);
6
+ }
7
+ export declare class FlowApi {
8
+ private readonly serverUrl;
9
+ private readonly token;
10
+ constructor(serverUrl: string, token: string);
11
+ private req;
12
+ me(): Promise<UserDTO>;
13
+ myWorkspaces(): Promise<WorkspaceDTO[]>;
14
+ listChannels(workspaceId: string): Promise<ChannelDTO[]>;
15
+ listMembers(workspaceId: string): Promise<WorkspaceMemberDTO[]>;
16
+ /** Post a message; mentions are parsed out of the body (server fans out notifications). */
17
+ sendMessage(channelId: string, body: string, threadRootId?: string, fileIds?: string[]): Promise<MessageDTO>;
18
+ editMessage(messageId: string, body: string): Promise<MessageDTO>;
19
+ deleteMessage(messageId: string): Promise<void>;
20
+ listMessages(channelId: string, limit?: number): Promise<MessagePage>;
21
+ listThread(rootId: string, limit?: number): Promise<MessageDTO[]>;
22
+ addReaction(messageId: string, emoji: string): Promise<unknown>;
23
+ /** Download a file attachment's original bytes (the agent is a channel member, so /v1/files authorizes it). */
24
+ downloadFile(fileId: string): Promise<Buffer>;
25
+ uploadFile(workspaceId: string, filename: string, mimeType: string, data: Buffer): Promise<FileDTO>;
26
+ }
27
+ /** Unauthenticated: consume an invite key → agent identity + token (shown once). */
28
+ export declare function registerAgent(serverUrl: string, input: {
29
+ inviteKey: string;
30
+ name?: string;
31
+ description?: string;
32
+ avatarUrl?: string;
33
+ }): Promise<AgentRegisterResponse>;
package/dist/api.js ADDED
@@ -0,0 +1,124 @@
1
+ // Minimal /v1 REST client speaking the agent bearer token.
2
+ import { randomUUID } from 'node:crypto';
3
+ const USER_MENTION_RE = /<@([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})>/g;
4
+ export class FlowApiError extends Error {
5
+ status;
6
+ code;
7
+ constructor(status, code, message) {
8
+ super(message);
9
+ this.status = status;
10
+ this.code = code;
11
+ this.name = 'FlowApiError';
12
+ }
13
+ }
14
+ async function parseError(res) {
15
+ let code = 'http_error';
16
+ let message = `${res.status} ${res.statusText}`;
17
+ try {
18
+ const body = (await res.json());
19
+ if (body.error) {
20
+ code = body.error.code ?? code;
21
+ message = body.error.message ?? message;
22
+ }
23
+ }
24
+ catch {
25
+ /* non-JSON error body */
26
+ }
27
+ throw new FlowApiError(res.status, code, message);
28
+ }
29
+ export class FlowApi {
30
+ serverUrl;
31
+ token;
32
+ constructor(serverUrl, token) {
33
+ this.serverUrl = serverUrl;
34
+ this.token = token;
35
+ }
36
+ async req(method, path, body) {
37
+ const res = await fetch(`${this.serverUrl}${path}`, {
38
+ method,
39
+ headers: {
40
+ authorization: `Bearer ${this.token}`,
41
+ ...(body !== undefined ? { 'content-type': 'application/json' } : {}),
42
+ },
43
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
44
+ });
45
+ if (!res.ok)
46
+ await parseError(res);
47
+ return (await res.json());
48
+ }
49
+ me() {
50
+ return this.req('GET', '/v1/me');
51
+ }
52
+ async myWorkspaces() {
53
+ const r = await this.req('GET', '/v1/me/workspaces');
54
+ return r.workspaces;
55
+ }
56
+ async listChannels(workspaceId) {
57
+ const r = await this.req('GET', `/v1/workspaces/${workspaceId}/channels`);
58
+ return r.channels;
59
+ }
60
+ async listMembers(workspaceId) {
61
+ const r = await this.req('GET', `/v1/workspaces/${workspaceId}/members`);
62
+ return r.members;
63
+ }
64
+ /** Post a message; mentions are parsed out of the body (server fans out notifications). */
65
+ sendMessage(channelId, body, threadRootId, fileIds) {
66
+ const mentions = [...new Set([...body.matchAll(USER_MENTION_RE)].map((m) => m[1]))];
67
+ return this.req('POST', `/v1/channels/${channelId}/messages`, {
68
+ clientMsgId: randomUUID(),
69
+ body: body.slice(0, 12000),
70
+ ...(threadRootId ? { threadRootId } : {}),
71
+ ...(fileIds?.length ? { fileIds } : {}),
72
+ ...(mentions.length ? { mentions: mentions.slice(0, 50) } : {}),
73
+ });
74
+ }
75
+ editMessage(messageId, body) {
76
+ return this.req('PATCH', `/v1/messages/${messageId}`, { body: body.slice(0, 12000) });
77
+ }
78
+ deleteMessage(messageId) {
79
+ return this.req('DELETE', `/v1/messages/${messageId}`);
80
+ }
81
+ listMessages(channelId, limit = 50) {
82
+ return this.req('GET', `/v1/channels/${channelId}/messages?limit=${limit}`);
83
+ }
84
+ async listThread(rootId, limit = 200) {
85
+ const r = await this.req('GET', `/v1/messages/${rootId}/thread?limit=${limit}`);
86
+ return r.messages;
87
+ }
88
+ addReaction(messageId, emoji) {
89
+ return this.req('PUT', `/v1/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`);
90
+ }
91
+ /** Download a file attachment's original bytes (the agent is a channel member, so /v1/files authorizes it). */
92
+ async downloadFile(fileId) {
93
+ const res = await fetch(`${this.serverUrl}/v1/files/${fileId}`, {
94
+ headers: { authorization: `Bearer ${this.token}` },
95
+ });
96
+ if (!res.ok)
97
+ await parseError(res);
98
+ return Buffer.from(await res.arrayBuffer());
99
+ }
100
+ async uploadFile(workspaceId, filename, mimeType, data) {
101
+ const form = new FormData();
102
+ form.append('file', new Blob([new Uint8Array(data)], { type: mimeType }), filename);
103
+ const res = await fetch(`${this.serverUrl}/v1/workspaces/${workspaceId}/files`, {
104
+ method: 'POST',
105
+ headers: { authorization: `Bearer ${this.token}` },
106
+ body: form,
107
+ });
108
+ if (!res.ok)
109
+ await parseError(res);
110
+ return (await res.json());
111
+ }
112
+ }
113
+ /** Unauthenticated: consume an invite key → agent identity + token (shown once). */
114
+ export async function registerAgent(serverUrl, input) {
115
+ const res = await fetch(`${serverUrl.replace(/\/+$/, '')}/v1/agents/register`, {
116
+ method: 'POST',
117
+ headers: { 'content-type': 'application/json' },
118
+ body: JSON.stringify(input),
119
+ });
120
+ if (!res.ok)
121
+ await parseError(res);
122
+ return (await res.json());
123
+ }
124
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { BridgeConfig } from './config.js';
2
+ export declare class AgentBridge {
3
+ private readonly cfg;
4
+ private readonly api;
5
+ private socket;
6
+ private me;
7
+ private workspace;
8
+ private channels;
9
+ private members;
10
+ private conversations;
11
+ private readonly sem;
12
+ private refreshTimer;
13
+ constructor(cfg: BridgeConfig);
14
+ log(msg: string): void;
15
+ start(): Promise<void>;
16
+ stop(): void;
17
+ private refreshDirectory;
18
+ private scheduleRefresh;
19
+ private handleEvent;
20
+ private handleMessage;
21
+ /** Sender gating + self/agent loop guard + event-scope filter. */
22
+ private inScope;
23
+ private convKey;
24
+ private handleReset;
25
+ private enqueue;
26
+ /** Serial within a conversation; the semaphore caps cross-conversation parallelism. */
27
+ private runConversation;
28
+ private processMessage;
29
+ private senderLabel;
30
+ private channelLabel;
31
+ private buildSystemPrompt;
32
+ /** Prompt = optional first-turn history + per-message sender metadata + body. */
33
+ private buildPrompt;
34
+ /**
35
+ * Message attachments (images, docs) → local temp copies the runtime can
36
+ * Read (Claude's Read tool renders images natively). One bad file never
37
+ * fails the turn; files persist for the session so --resume references
38
+ * stay valid. Demo runtime never spawns a CLI, so skip the downloads.
39
+ */
40
+ private downloadAttachments;
41
+ /** History for a session's first turn: the thread so far, or recent channel/DM messages. */
42
+ private fetchHistory;
43
+ /** Per-run MCP config: the flow stdio server with conversation context in env. */
44
+ private writeMcpConfig;
45
+ }
package/dist/bridge.js ADDED
@@ -0,0 +1,336 @@
1
+ // The bridge daemon: consume Flow events over WS, exec a coding-agent CLI
2
+ // headlessly per conversation, post the reply back (AGENTS_DESIGN.md).
3
+ //
4
+ // One CLI session per conversation: (channelId, threadRootId) → session uuid,
5
+ // `--session-id` on the first turn, `--resume` after. Conversations run
6
+ // concurrently (cap N), messages within one conversation run serially.
7
+ import fs from 'node:fs';
8
+ import os from 'node:os';
9
+ import path from 'node:path';
10
+ import { randomUUID } from 'node:crypto';
11
+ import { fileURLToPath } from 'node:url';
12
+ import { FlowApi } from './api.js';
13
+ import { FlowSocket } from './gateway.js';
14
+ import { ProgressReporter } from './progress.js';
15
+ import { runRuntime } from './runtime.js';
16
+ const THINKING_PREFIX = '🤖 *thinking…*';
17
+ class Semaphore {
18
+ slots;
19
+ waiters = [];
20
+ constructor(slots) {
21
+ this.slots = slots;
22
+ }
23
+ async acquire() {
24
+ if (this.slots > 0) {
25
+ this.slots -= 1;
26
+ return;
27
+ }
28
+ await new Promise((resolve) => this.waiters.push(resolve));
29
+ }
30
+ release() {
31
+ const next = this.waiters.shift();
32
+ if (next)
33
+ next();
34
+ else
35
+ this.slots += 1;
36
+ }
37
+ }
38
+ export class AgentBridge {
39
+ cfg;
40
+ api;
41
+ socket;
42
+ me;
43
+ workspace;
44
+ channels = new Map();
45
+ members = new Map();
46
+ conversations = new Map();
47
+ sem;
48
+ refreshTimer = null;
49
+ constructor(cfg) {
50
+ this.cfg = cfg;
51
+ this.api = new FlowApi(cfg.serverUrl, cfg.agentToken);
52
+ this.sem = new Semaphore(cfg.concurrency);
53
+ }
54
+ log(msg) {
55
+ console.log(`[bridge ${new Date().toISOString()}] ${msg}`);
56
+ }
57
+ async start() {
58
+ this.me = await this.api.me();
59
+ if (!this.me.isAgent)
60
+ this.log('warning: token belongs to a non-agent user');
61
+ const wss = await this.api.myWorkspaces();
62
+ if (wss.length === 0)
63
+ throw new Error('agent belongs to no workspace');
64
+ this.workspace = wss[0];
65
+ await this.refreshDirectory();
66
+ this.log(`${this.me.displayName} <@${this.me.id}> online in "${this.workspace.name}" — ` +
67
+ `scope=${this.cfg.eventScope}+DMs progress=${this.cfg.progress} runtime=${this.cfg.runtime.kind} cwd=${this.cfg.runtime.cwd}`);
68
+ this.socket = new FlowSocket({
69
+ serverUrl: this.cfg.serverUrl,
70
+ token: this.cfg.agentToken,
71
+ onEvent: (ev) => this.handleEvent(ev),
72
+ log: (m) => this.log(m),
73
+ });
74
+ this.socket.connect();
75
+ }
76
+ stop() {
77
+ this.socket?.close();
78
+ }
79
+ async refreshDirectory() {
80
+ const [chans, members] = await Promise.all([
81
+ this.api.listChannels(this.workspace.id),
82
+ this.api.listMembers(this.workspace.id),
83
+ ]);
84
+ this.channels = new Map(chans.map((c) => [c.id, c]));
85
+ this.members = new Map(members.map((m) => [m.userId, m]));
86
+ }
87
+ scheduleRefresh() {
88
+ if (this.refreshTimer)
89
+ return;
90
+ this.refreshTimer = setTimeout(() => {
91
+ this.refreshTimer = null;
92
+ this.refreshDirectory().catch((err) => this.log(`directory refresh failed: ${err.message}`));
93
+ }, 1000);
94
+ this.refreshTimer.unref();
95
+ }
96
+ handleEvent(ev) {
97
+ if (ev.type === 'member.joined' || ev.type === 'member.left' || ev.type === 'channel.created') {
98
+ this.scheduleRefresh();
99
+ return;
100
+ }
101
+ if (ev.type !== 'message.created' && ev.type !== 'thread.reply')
102
+ return;
103
+ void this.handleMessage(ev.data);
104
+ }
105
+ async handleMessage(msg) {
106
+ // a message can beat the debounced directory refresh (e.g. a brand-new DM)
107
+ if (!this.channels.has(msg.channelId) || !this.members.has(msg.userId)) {
108
+ await this.refreshDirectory().catch((err) => this.log(`directory refresh failed: ${err.message}`));
109
+ }
110
+ if (!this.inScope(msg))
111
+ return;
112
+ if (msg.body.trim() === '/reset')
113
+ return this.handleReset(msg);
114
+ this.enqueue(msg);
115
+ }
116
+ /** Sender gating + self/agent loop guard + event-scope filter. */
117
+ inScope(msg) {
118
+ if (msg.userId === this.me.id)
119
+ return false; // never our own messages (incl. MCP-sent)
120
+ if (msg.deletedAt)
121
+ return false;
122
+ const sender = this.members.get(msg.userId);
123
+ if (!sender)
124
+ return false; // only workspace members
125
+ if (sender.isAgent && !this.cfg.respondToAgents)
126
+ return false; // agent-to-agent loop guard
127
+ const chan = this.channels.get(msg.channelId);
128
+ if (!chan?.isMember)
129
+ return false; // only channels we're in
130
+ if (chan.kind === 'dm' || chan.kind === 'group_dm')
131
+ return true;
132
+ if (msg.body.startsWith(THINKING_PREFIX))
133
+ return false; // another agent's status line
134
+ if (this.cfg.eventScope === 'all')
135
+ return true;
136
+ return msg.body.includes(`<@${this.me.id}>`); // @-mention
137
+ }
138
+ convKey(msg) {
139
+ return `${msg.channelId}|${msg.threadRootId ?? ''}`;
140
+ }
141
+ async handleReset(msg) {
142
+ this.conversations.delete(this.convKey(msg));
143
+ await this.api
144
+ .sendMessage(msg.channelId, '🤖 context reset — the next message starts a fresh session.', msg.threadRootId ?? undefined)
145
+ .catch((err) => this.log(`reset reply failed: ${err.message}`));
146
+ }
147
+ enqueue(msg) {
148
+ const key = this.convKey(msg);
149
+ let conv = this.conversations.get(key);
150
+ if (!conv) {
151
+ conv = { sessionId: randomUUID(), started: false, queue: [], running: false };
152
+ this.conversations.set(key, conv);
153
+ }
154
+ conv.queue.push(msg);
155
+ if (!conv.running)
156
+ void this.runConversation(key, conv);
157
+ }
158
+ /** Serial within a conversation; the semaphore caps cross-conversation parallelism. */
159
+ async runConversation(key, conv) {
160
+ conv.running = true;
161
+ try {
162
+ while (conv.queue.length > 0) {
163
+ const msg = conv.queue.shift();
164
+ await this.sem.acquire();
165
+ try {
166
+ await this.processMessage(conv, msg);
167
+ }
168
+ catch (err) {
169
+ this.log(`processing failed: ${err.message}`);
170
+ }
171
+ finally {
172
+ this.sem.release();
173
+ }
174
+ // /reset mid-run replaces the map entry; stop pumping the stale one
175
+ if (this.conversations.get(key) !== conv)
176
+ break;
177
+ }
178
+ }
179
+ finally {
180
+ conv.running = false;
181
+ if (conv.queue.length > 0 && this.conversations.get(key) === conv)
182
+ void this.runConversation(key, conv);
183
+ }
184
+ }
185
+ async processMessage(conv, msg) {
186
+ const progress = new ProgressReporter(this.api, this.socket, this.cfg.progress, msg.channelId, msg.threadRootId ?? undefined, (m) => this.log(m));
187
+ progress.start();
188
+ const mcpConfigPath = this.cfg.runtime.mcp ? this.writeMcpConfig(msg) : undefined;
189
+ try {
190
+ const prompt = await this.buildPrompt(conv, msg);
191
+ const result = await runRuntime(this.cfg.runtime, {
192
+ sessionId: conv.sessionId,
193
+ resume: conv.started,
194
+ prompt,
195
+ systemPrompt: this.buildSystemPrompt(msg, mcpConfigPath !== undefined),
196
+ mcpConfigPath,
197
+ onToolStep: (step) => progress.onStep(step),
198
+ log: (m) => this.log(m),
199
+ });
200
+ await progress.finish();
201
+ if (result.ok) {
202
+ conv.started = true;
203
+ const text = result.text.trim();
204
+ if (text.length > 0) {
205
+ await this.api.sendMessage(msg.channelId, text, msg.threadRootId ?? undefined);
206
+ }
207
+ }
208
+ else {
209
+ this.log(`runtime error: ${result.error ?? 'unknown'}`);
210
+ await this.api
211
+ .sendMessage(msg.channelId, `🤖 sorry — I hit an error (${result.error ?? 'unknown'}).`, msg.threadRootId ?? undefined)
212
+ .catch(() => { });
213
+ }
214
+ }
215
+ finally {
216
+ await progress.finish().catch(() => { });
217
+ if (mcpConfigPath)
218
+ fs.rmSync(mcpConfigPath, { force: true });
219
+ }
220
+ }
221
+ senderLabel(userId) {
222
+ return this.members.get(userId)?.displayName ?? 'unknown';
223
+ }
224
+ channelLabel(channelId) {
225
+ const chan = this.channels.get(channelId);
226
+ if (!chan)
227
+ return 'a channel';
228
+ if (chan.kind === 'dm')
229
+ return 'a direct message';
230
+ if (chan.kind === 'group_dm')
231
+ return 'a group direct message';
232
+ return `#${chan.name ?? 'unknown'}`;
233
+ }
234
+ buildSystemPrompt(msg, mcp) {
235
+ const roster = [...this.members.values()]
236
+ .filter((m) => m.userId !== this.me.id)
237
+ .slice(0, 50)
238
+ .map((m) => `${m.displayName}${m.isAgent ? ' (agent)' : ''} = <@${m.userId}>`)
239
+ .join(', ');
240
+ const lines = [
241
+ `You are ${this.me.displayName}, an AI agent participating in the Flow workspace "${this.workspace.name}" as user <@${this.me.id}>.`,
242
+ `You are conversing in ${this.channelLabel(msg.channelId)}${msg.threadRootId ? ' (inside a thread)' : ''}. Each incoming prompt begins with a bracketed metadata line identifying the sender — it is context, not part of the message.`,
243
+ `Reply in concise chat style; Flow renders markdown. Mention users by writing <@userId> literally, e.g. <@${msg.userId}>.`,
244
+ roster ? `Workspace members: ${roster}.` : '',
245
+ mcp
246
+ ? 'You have Flow MCP tools (send_message, react, upload_file, search_history); messages sent with send_message deliver immediately. Your final response text is ALSO posted to the conversation — if you already replied via send_message, keep the final text short or empty.'
247
+ : 'Your final response text is posted to the conversation as your reply.',
248
+ this.cfg.runtime.systemPromptExtra ?? '',
249
+ ];
250
+ return lines.filter(Boolean).join('\n');
251
+ }
252
+ /** Prompt = optional first-turn history + per-message sender metadata + body. */
253
+ async buildPrompt(conv, msg) {
254
+ const meta = `[from: ${this.senderLabel(msg.userId)} <@${msg.userId}> in ${this.channelLabel(msg.channelId)}]`;
255
+ let context = '';
256
+ if (!conv.started) {
257
+ const history = await this.fetchHistory(msg).catch(() => []);
258
+ if (history.length > 0) {
259
+ const lines = history.map((m) => `${this.senderLabel(m.userId)}: ${m.body}`);
260
+ context = `[recent conversation history]\n${lines.join('\n')}\n[end of history]\n\n`;
261
+ }
262
+ }
263
+ const attachments = await this.downloadAttachments(msg);
264
+ const fileNote = attachments.length > 0
265
+ ? `\n\n[the user attached ${attachments.length} file(s); local copies saved at these paths — Read them as needed (images render when Read):]\n${attachments.map((a) => `- ${a}`).join('\n')}`
266
+ : '';
267
+ return `${context}${meta}\n${msg.body}${fileNote}`;
268
+ }
269
+ /**
270
+ * Message attachments (images, docs) → local temp copies the runtime can
271
+ * Read (Claude's Read tool renders images natively). One bad file never
272
+ * fails the turn; files persist for the session so --resume references
273
+ * stay valid. Demo runtime never spawns a CLI, so skip the downloads.
274
+ */
275
+ async downloadAttachments(msg) {
276
+ if (this.cfg.runtime.kind === 'demo' || msg.files.length === 0)
277
+ return [];
278
+ const dir = path.join(os.tmpdir(), 'flow-attachments', this.me.id);
279
+ fs.mkdirSync(dir, { recursive: true });
280
+ const out = [];
281
+ for (const f of msg.files) {
282
+ const safe = f.name.replace(/[^\w.\-]+/g, '_').slice(-80) || 'file';
283
+ const dest = path.join(dir, `${f.id}-${safe}`);
284
+ try {
285
+ if (!fs.existsSync(dest))
286
+ fs.writeFileSync(dest, await this.api.downloadFile(f.id), { mode: 0o600 });
287
+ out.push(`${dest} (${f.mimeType}, ${f.sizeBytes} bytes)`);
288
+ }
289
+ catch (err) {
290
+ this.log(`attachment download failed for ${f.name}: ${err.message}`);
291
+ }
292
+ }
293
+ return out;
294
+ }
295
+ /** History for a session's first turn: the thread so far, or recent channel/DM messages. */
296
+ async fetchHistory(msg) {
297
+ let msgs;
298
+ if (msg.threadRootId) {
299
+ msgs = await this.api.listThread(msg.threadRootId);
300
+ }
301
+ else {
302
+ const page = await this.api.listMessages(msg.channelId, 15);
303
+ msgs = page.messages;
304
+ }
305
+ return msgs
306
+ .filter((m) => m.id !== msg.id && !m.deletedAt && !m.body.startsWith(THINKING_PREFIX))
307
+ .slice(-15);
308
+ }
309
+ /** Per-run MCP config: the flow stdio server with conversation context in env. */
310
+ writeMcpConfig(msg) {
311
+ const entry = fileURLToPath(new URL('./index.js', import.meta.url));
312
+ if (!fs.existsSync(entry)) {
313
+ this.log('mcp disabled: built entrypoint not found (run pnpm build in packages/agent-bridge)');
314
+ return undefined;
315
+ }
316
+ const cfg = {
317
+ mcpServers: {
318
+ flow: {
319
+ command: process.execPath,
320
+ args: [entry, 'mcp'],
321
+ env: {
322
+ FLOW_SERVER_URL: this.cfg.serverUrl,
323
+ FLOW_AGENT_TOKEN: this.cfg.agentToken,
324
+ FLOW_WORKSPACE_ID: this.workspace.id,
325
+ FLOW_CHANNEL_ID: msg.channelId,
326
+ FLOW_THREAD_ROOT_ID: msg.threadRootId ?? '',
327
+ },
328
+ },
329
+ },
330
+ };
331
+ const p = path.join(os.tmpdir(), `flow-mcp-${randomUUID()}.json`);
332
+ fs.writeFileSync(p, JSON.stringify(cfg));
333
+ return p;
334
+ }
335
+ }
336
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}