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 +89 -0
- package/dist/api.d.ts +33 -0
- package/dist/api.js +124 -0
- package/dist/api.js.map +1 -0
- package/dist/bridge.d.ts +45 -0
- package/dist/bridge.js +336 -0
- package/dist/bridge.js.map +1 -0
- package/dist/config.d.ts +45 -0
- package/dist/config.js +65 -0
- package/dist/config.js.map +1 -0
- package/dist/gateway.d.ts +20 -0
- package/dist/gateway.js +68 -0
- package/dist/gateway.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server.d.ts +1 -0
- package/dist/mcp-server.js +186 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/progress.d.ts +23 -0
- package/dist/progress.js +78 -0
- package/dist/progress.js.map +1 -0
- package/dist/runtime.d.ts +36 -0
- package/dist/runtime.js +177 -0
- package/dist/runtime.js.map +1 -0
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +86 -0
- package/dist/setup.js.map +1 -0
- package/package.json +48 -0
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
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge.d.ts
ADDED
|
@@ -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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"}
|