@hasna/bridge 0.1.2 → 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 +79 -3
- package/dist/cli/index.js +786 -26
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +630 -11
- package/dist/lib/agents.d.ts +14 -1
- package/dist/lib/agents.d.ts.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/daemon.d.ts.map +1 -1
- package/dist/lib/doctor.d.ts.map +1 -1
- package/dist/lib/imessage.d.ts +36 -0
- package/dist/lib/imessage.d.ts.map +1 -0
- package/dist/lib/router.d.ts.map +1 -1
- package/dist/lib/sessions.d.ts +48 -0
- package/dist/lib/sessions.d.ts.map +1 -0
- package/dist/lib/state.d.ts +8 -1
- package/dist/lib/state.d.ts.map +1 -1
- package/dist/lib/telegram.d.ts +1 -0
- package/dist/lib/telegram.d.ts.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +584 -7
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/architecture.md +69 -15
- package/docs/session-bridge-plan.md +204 -0
- package/package.json +1 -1
package/dist/types.d.ts
CHANGED
|
@@ -27,6 +27,13 @@ export interface WebhookChannelConfig extends BaseChannelConfig {
|
|
|
27
27
|
export interface IMessageChannelConfig extends BaseChannelConfig {
|
|
28
28
|
kind: "imessage";
|
|
29
29
|
account?: string;
|
|
30
|
+
serviceName?: string;
|
|
31
|
+
defaultHandle?: string;
|
|
32
|
+
allowedHandles?: string[];
|
|
33
|
+
allowAllHandles?: boolean;
|
|
34
|
+
receiveMode?: "disabled" | "chat-db";
|
|
35
|
+
chatDbPath?: string;
|
|
36
|
+
pollLimit?: number;
|
|
30
37
|
}
|
|
31
38
|
export type ChannelConfig = TelegramChannelConfig | ConsoleChannelConfig | WebhookChannelConfig | IMessageChannelConfig;
|
|
32
39
|
export interface ProfileConfig {
|
|
@@ -75,13 +82,69 @@ export interface BridgeMessage {
|
|
|
75
82
|
channelId: string;
|
|
76
83
|
text: string;
|
|
77
84
|
chatId?: string;
|
|
85
|
+
threadId?: string;
|
|
86
|
+
responseTargetId?: string;
|
|
78
87
|
from?: string;
|
|
79
88
|
receivedAt: string;
|
|
80
89
|
raw?: unknown;
|
|
81
90
|
}
|
|
91
|
+
export type BridgeSessionStatus = "active" | "paused" | "closed";
|
|
92
|
+
export type AgentSessionMode = "durable" | "compatibility";
|
|
93
|
+
export interface AgentSessionRef {
|
|
94
|
+
kind: AgentKind;
|
|
95
|
+
mode: AgentSessionMode;
|
|
96
|
+
refId?: string;
|
|
97
|
+
createdAt?: string;
|
|
98
|
+
updatedAt?: string;
|
|
99
|
+
detail?: string;
|
|
100
|
+
}
|
|
101
|
+
export interface BridgeSession {
|
|
102
|
+
id: string;
|
|
103
|
+
agentId: string;
|
|
104
|
+
profileId?: string;
|
|
105
|
+
cwd?: string;
|
|
106
|
+
title?: string;
|
|
107
|
+
status: BridgeSessionStatus;
|
|
108
|
+
createdAt: string;
|
|
109
|
+
updatedAt: string;
|
|
110
|
+
lastMessageAt?: string;
|
|
111
|
+
agentSession?: AgentSessionRef;
|
|
112
|
+
}
|
|
113
|
+
export interface BridgeBinding {
|
|
114
|
+
id: string;
|
|
115
|
+
channelId: string;
|
|
116
|
+
conversationId: string;
|
|
117
|
+
activeSessionId: string;
|
|
118
|
+
defaultSessionId?: string;
|
|
119
|
+
createdAt: string;
|
|
120
|
+
updatedAt: string;
|
|
121
|
+
authorization?: {
|
|
122
|
+
chatId?: string;
|
|
123
|
+
from?: string;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
export type LedgerStatus = "processing" | "agent_completed" | "delivered" | "skipped" | "unauthorized" | "failed";
|
|
127
|
+
export interface MessageLedgerEntry {
|
|
128
|
+
id: string;
|
|
129
|
+
channelId: string;
|
|
130
|
+
messageId: string;
|
|
131
|
+
conversationId?: string;
|
|
132
|
+
sessionId?: string;
|
|
133
|
+
status: LedgerStatus;
|
|
134
|
+
attempts: number;
|
|
135
|
+
firstSeenAt: string;
|
|
136
|
+
updatedAt: string;
|
|
137
|
+
terminalAt?: string;
|
|
138
|
+
error?: string;
|
|
139
|
+
responseText?: string;
|
|
140
|
+
deliveredResponse?: boolean;
|
|
141
|
+
agentExitCode?: number | null;
|
|
142
|
+
agentTimedOut?: boolean;
|
|
143
|
+
}
|
|
82
144
|
export interface AgentRunInput {
|
|
83
145
|
message: BridgeMessage;
|
|
84
146
|
route: RouteConfig;
|
|
147
|
+
session?: BridgeSession;
|
|
85
148
|
}
|
|
86
149
|
export interface AgentRunResult {
|
|
87
150
|
agentId: string;
|
|
@@ -97,6 +160,16 @@ export interface RoutedMessageResult {
|
|
|
97
160
|
agent: AgentRunResult;
|
|
98
161
|
deliveredResponse?: boolean;
|
|
99
162
|
}
|
|
163
|
+
export interface SessionMessageResult {
|
|
164
|
+
kind: "session";
|
|
165
|
+
session?: BridgeSession;
|
|
166
|
+
binding?: BridgeBinding;
|
|
167
|
+
conversationId?: string;
|
|
168
|
+
agent?: AgentRunResult;
|
|
169
|
+
deliveredResponse?: boolean;
|
|
170
|
+
status: "delivered" | "no_session" | "paused" | "closed" | "unauthorized" | "no_output" | "failed";
|
|
171
|
+
message?: string;
|
|
172
|
+
}
|
|
100
173
|
export interface DoctorCheck {
|
|
101
174
|
name: string;
|
|
102
175
|
ok: boolean;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,EAAG,CAAU,CAAC;AAEzC,eAAO,MAAM,aAAa,yDAA0D,CAAC;AACrF,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,eAAO,MAAM,WAAW,uDAAwD,CAAC;AACjF,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,IAAI,EAAE,SAAS,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,EAAG,CAAU,CAAC;AAEzC,eAAO,MAAM,aAAa,yDAA0D,CAAC;AACrF,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,eAAO,MAAM,WAAW,uDAAwD,CAAC;AACjF,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,IAAI,EAAE,SAAS,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GACrB,qBAAqB,GACrB,oBAAoB,GACpB,oBAAoB,GACpB,qBAAqB,CAAC;AAE1B,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,cAAc,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACxC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AACjE,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,eAAe,CAAC;AAE3D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,eAAe,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,iBAAiB,GAAG,WAAW,GAAG,SAAS,GAAG,cAAc,GAAG,QAAQ,CAAC;AAElH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,cAAc,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,MAAM,EAAE,WAAW,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,cAAc,GAAG,WAAW,GAAG,QAAQ,CAAC;IACnG,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB"}
|
package/docs/architecture.md
CHANGED
|
@@ -1,35 +1,50 @@
|
|
|
1
1
|
# Bridge Architecture
|
|
2
2
|
|
|
3
|
-
`bridge` separates channel connectors from agent adapters.
|
|
3
|
+
`bridge` separates channel connectors from agent adapters. In `0.2.x`, the
|
|
4
|
+
primary product model is session-backed routing: external conversations bind to
|
|
5
|
+
durable bridge sessions, and bridge sessions bind to agent sessions.
|
|
4
6
|
|
|
5
7
|
Channels own external transport details such as Telegram bot tokens, chat IDs,
|
|
6
|
-
webhooks, or
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
webhooks, or iMessage transport. Agents own local execution details such as
|
|
9
|
+
Codewith auth profiles, Claude profile homes, AIcopilot state roots, cwd, and
|
|
10
|
+
environment. Bindings connect a channel conversation to a durable bridge
|
|
11
|
+
session. Routes remain a compatibility surface for explicit stateless tests.
|
|
9
12
|
|
|
10
|
-
The package deliberately keeps Telegram-specific
|
|
11
|
-
Claude, and AIcopilot integrations. Agent adapters receive
|
|
12
|
-
and return
|
|
13
|
-
to receive and deliver messages.
|
|
13
|
+
The package deliberately keeps Telegram-specific or iMessage-specific behavior
|
|
14
|
+
out of Codewith, Claude, and AIcopilot integrations. Agent adapters receive
|
|
15
|
+
normalized session messages and return final responses or stream events. Channel
|
|
16
|
+
connectors decide how to receive, authorize, normalize, and deliver messages.
|
|
14
17
|
|
|
15
18
|
## Data Model
|
|
16
19
|
|
|
17
20
|
- `channels`: external message transports.
|
|
18
21
|
- `profiles`: reusable identity/state settings for an agent family.
|
|
19
22
|
- `agents`: runnable targets, usually pointing at one profile.
|
|
20
|
-
- `
|
|
23
|
+
- `sessions`: bridge-owned conversations with one agent target and optional
|
|
24
|
+
external agent session reference.
|
|
25
|
+
- `bindings`: mapping from normalized external conversation IDs to active
|
|
26
|
+
bridge sessions.
|
|
27
|
+
- `messageLedger`: idempotency and retry records for inbound channel messages.
|
|
28
|
+
- `cursors`: per-channel offsets committed after terminal processing.
|
|
29
|
+
- `routes`: compatibility mapping rules from channel messages to agents.
|
|
30
|
+
|
|
31
|
+
Runtime state uses `schemaVersion: 2` and is backward-compatible with the
|
|
32
|
+
`0.1.x` state shape that only stored `telegramOffsets`.
|
|
21
33
|
|
|
22
34
|
## Profile Model
|
|
23
35
|
|
|
24
|
-
Codewith profiles use `authProfile
|
|
25
|
-
|
|
36
|
+
Codewith profiles use `authProfile`. The durable adapter should use Codewith's
|
|
37
|
+
background-agent surface where a noninteractive create/send/resume flow is
|
|
38
|
+
available. `codewith --auth-profile <name> --cd <cwd> exec <prompt>` is
|
|
39
|
+
compatibility mode because it is a one-shot process and does not preserve
|
|
40
|
+
agent-side conversation context.
|
|
26
41
|
|
|
27
42
|
Claude profiles can use `home` or custom env vars so multiple accounts do not
|
|
28
43
|
share local state.
|
|
29
44
|
|
|
30
|
-
AIcopilot profiles currently use cwd/env/command isolation. The
|
|
31
|
-
|
|
32
|
-
|
|
45
|
+
AIcopilot profiles currently use cwd/env/command isolation. The adapter must add
|
|
46
|
+
or consume first-class create/send/events session support before AIcopilot can
|
|
47
|
+
be presented as a durable bridge session target.
|
|
33
48
|
|
|
34
49
|
## Telegram
|
|
35
50
|
|
|
@@ -48,7 +63,22 @@ MCP config inspection redacts profile and agent environment values so local
|
|
|
48
63
|
secrets are not exposed through `bridge_config`.
|
|
49
64
|
|
|
50
65
|
Long-poll offsets are persisted in a private state file so process restarts do
|
|
51
|
-
not replay already-seen updates and re-run agents.
|
|
66
|
+
not replay already-seen terminal updates and re-run agents. In session mode,
|
|
67
|
+
offsets must be committed only after the inbound message reaches a terminal
|
|
68
|
+
ledger state: delivered, intentionally skipped, or unauthorized. Failed
|
|
69
|
+
messages remain retryable and do not advance the Telegram offset.
|
|
70
|
+
|
|
71
|
+
If the agent succeeds and outbound delivery fails, the ledger stores the final
|
|
72
|
+
response in `agent_completed`; retry attempts deliver the stored response rather
|
|
73
|
+
than invoking the agent again.
|
|
74
|
+
|
|
75
|
+
When a Telegram chat has an active binding, plain text routes to that session.
|
|
76
|
+
When no binding exists, the bridge can reply with setup instructions and does
|
|
77
|
+
not invoke an agent. Route matching is still available as compatibility fallback
|
|
78
|
+
for explicit configured routes.
|
|
79
|
+
|
|
80
|
+
Telegram forum topics are part of the normalized conversation ID:
|
|
81
|
+
`telegram:<channelId>:<chatId>:<messageThreadId>`.
|
|
52
82
|
|
|
53
83
|
`BRIDGE_TELEGRAM_API_BASE` can override the Telegram API origin for local tests.
|
|
54
84
|
The override accepts only `http` or `https` URLs without credentials. It is not
|
|
@@ -80,3 +110,27 @@ errors.
|
|
|
80
110
|
`bridge serve` handles per-channel poll errors without exiting in long-running
|
|
81
111
|
mode. `serve --once` still fails fast so health checks and tests can catch
|
|
82
112
|
misconfiguration.
|
|
113
|
+
|
|
114
|
+
## iMessage
|
|
115
|
+
|
|
116
|
+
iMessage support is local macOS-only. The adapter must send through Messages
|
|
117
|
+
automation and receive through local Messages state or a documented helper mode.
|
|
118
|
+
It must fail closed when Automation or Full Disk Access permissions are missing.
|
|
119
|
+
Diagnostics should report the missing permission without reading or logging
|
|
120
|
+
private message contents unnecessarily.
|
|
121
|
+
|
|
122
|
+
The implemented adapter sends with `osascript` and can optionally receive by
|
|
123
|
+
polling `~/Library/Messages/chat.db` when `receiveMode` is `chat-db`. The send
|
|
124
|
+
script honors `account` when configured. The receive path filters out messages
|
|
125
|
+
from the local user, enforces `allowedHandles` unless `allowAllHandles` is
|
|
126
|
+
explicitly set, scans ahead past disallowed rows to avoid cursor starvation, and
|
|
127
|
+
keeps local Messages chat GUIDs so group chats do not collapse to a single
|
|
128
|
+
sender handle. Daemon cursors store the last processed Messages row ID per
|
|
129
|
+
channel.
|
|
130
|
+
|
|
131
|
+
## MCP
|
|
132
|
+
|
|
133
|
+
MCP must expose session-first tools for listing, creating, attaching, sending
|
|
134
|
+
to, and inspecting bridge sessions. Route-based MCP tools are compatibility-only
|
|
135
|
+
after `0.2.x`; MCP clients should use `bridge_session_route_message` when they
|
|
136
|
+
want normal inbound message behavior through bindings.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Session-Backed Multi-Channel Bridge Plan
|
|
2
|
+
|
|
3
|
+
This is the working plan for the `0.2.x` bridge release. The repo folder is
|
|
4
|
+
`open-bridge`, the package is `@hasna/bridge`, and the CLI remains `bridge`.
|
|
5
|
+
|
|
6
|
+
## Goal
|
|
7
|
+
|
|
8
|
+
`bridge` should connect external conversations to durable agent sessions. A
|
|
9
|
+
Telegram chat, iMessage thread, Slack channel, or future channel should map to a
|
|
10
|
+
bridge conversation and then to a Codewith, Claude Code, or AIcopilot session.
|
|
11
|
+
Normal messages go to the active bound session. Prefixes are only acceptable as
|
|
12
|
+
an optional compatibility route, not as the main product model.
|
|
13
|
+
|
|
14
|
+
## Target Model
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
channel event
|
|
18
|
+
-> normalized bridge conversation
|
|
19
|
+
-> durable bridge binding
|
|
20
|
+
-> agent session adapter
|
|
21
|
+
-> streamed or final channel response
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- Channels normalize inbound events and deliver outbound text.
|
|
25
|
+
- Bindings map external conversation IDs to bridge sessions.
|
|
26
|
+
- Agent adapters own account/profile state and send messages to a durable agent
|
|
27
|
+
session.
|
|
28
|
+
- Session commands manage control flow; ordinary text is user intent.
|
|
29
|
+
|
|
30
|
+
## Versioned State
|
|
31
|
+
|
|
32
|
+
`0.2.x` must add versioned state separate from static config:
|
|
33
|
+
|
|
34
|
+
- `schemaVersion`: state schema version with forward-only migration.
|
|
35
|
+
- `sessions`: bridge-owned session records keyed by stable session ID.
|
|
36
|
+
- `bindings`: external conversation bindings keyed by normalized channel
|
|
37
|
+
conversation ID.
|
|
38
|
+
- `messageLedger`: per-channel inbound processing records for idempotency,
|
|
39
|
+
retries, and delivery status.
|
|
40
|
+
- `cursors`: per-channel cursor/offset state, committed only after the inbound
|
|
41
|
+
message has reached a terminal routing state.
|
|
42
|
+
|
|
43
|
+
Session records must include agent ID, profile ID, cwd, status
|
|
44
|
+
(`active`, `paused`, `closed`), external agent session reference, timestamps,
|
|
45
|
+
last activity, timeout policy, and compatibility-mode marker when an adapter
|
|
46
|
+
falls back to one-shot execution.
|
|
47
|
+
|
|
48
|
+
Binding records must include channel ID, normalized conversation ID, active
|
|
49
|
+
session ID, optional default session ID, created/updated timestamps, and an
|
|
50
|
+
authorization snapshot such as Telegram chat ID or iMessage handle.
|
|
51
|
+
|
|
52
|
+
## Idempotency And Retry
|
|
53
|
+
|
|
54
|
+
- Inbound message processing writes a ledger row before invoking an agent.
|
|
55
|
+
- Channel cursors advance only after a message is delivered, intentionally
|
|
56
|
+
skipped, or rejected as unauthorized. Failed messages remain retryable until a
|
|
57
|
+
retry-exhaustion policy is added.
|
|
58
|
+
- Agent failure leaves a retryable `failed` ledger state.
|
|
59
|
+
- Outbound send failure after an agent succeeds leaves an `agent_completed`
|
|
60
|
+
ledger state with the generated response, so retries do not re-run the agent.
|
|
61
|
+
- Replayed updates must not run the agent twice after a successful delivery.
|
|
62
|
+
- Daemon restart must recover in-progress ledger rows without losing prompts.
|
|
63
|
+
|
|
64
|
+
## Commands
|
|
65
|
+
|
|
66
|
+
Planned CLI surface:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
bridge sessions list
|
|
70
|
+
bridge sessions show <id>
|
|
71
|
+
bridge sessions create --agent codewith --cwd /repo
|
|
72
|
+
bridge sessions attach <id> --channel telegram-main --conversation 1225577096
|
|
73
|
+
bridge sessions use <id> --channel telegram-main --conversation 1225577096
|
|
74
|
+
bridge sessions detach --channel telegram-main --conversation 1225577096
|
|
75
|
+
bridge sessions close <id>
|
|
76
|
+
bridge sessions pause <id>
|
|
77
|
+
bridge sessions resume <id>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Telegram and future command-capable channels should expose the same session
|
|
81
|
+
controls as slash commands:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
/sessions
|
|
85
|
+
/new
|
|
86
|
+
/use <id>
|
|
87
|
+
/attach <id>
|
|
88
|
+
/detach
|
|
89
|
+
/pause
|
|
90
|
+
/resume
|
|
91
|
+
/close
|
|
92
|
+
/help
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Channels
|
|
96
|
+
|
|
97
|
+
Telegram:
|
|
98
|
+
|
|
99
|
+
- Plain messages route to the active session for that chat.
|
|
100
|
+
- If no active session exists, the bot responds with a short setup prompt and
|
|
101
|
+
does not invoke an agent.
|
|
102
|
+
- Slash commands manage sessions and never require a `cw` prefix.
|
|
103
|
+
- Group chats must support `/command@BotName`, topic/thread IDs when Telegram
|
|
104
|
+
provides them, and unauthorized command rejection.
|
|
105
|
+
- Unknown slash commands are treated as control errors, not agent prompts.
|
|
106
|
+
- A literal agent prompt that starts with `/` must require an explicit escape
|
|
107
|
+
command or compatibility route, so control messages and prompts do not collide.
|
|
108
|
+
- Existing route-based behavior can remain for explicit compatibility tests.
|
|
109
|
+
|
|
110
|
+
iMessage:
|
|
111
|
+
|
|
112
|
+
- The first adapter is local macOS-only.
|
|
113
|
+
- Sending uses Messages automation through `osascript` and reports TCC
|
|
114
|
+
Automation failures clearly.
|
|
115
|
+
- Receiving uses local Messages state with a per-chat cursor or a documented
|
|
116
|
+
helper mode. It must fail closed when Full Disk Access or Messages data access
|
|
117
|
+
is unavailable.
|
|
118
|
+
- Conversation IDs are normalized from service, chat GUID, handle, and optional
|
|
119
|
+
account so multiple local accounts do not collide.
|
|
120
|
+
- Permission diagnostics explain needed macOS Automation and Full Disk Access
|
|
121
|
+
grants without storing private message contents in config.
|
|
122
|
+
- Tests must be skip-safe on non-macOS and on macOS machines without the needed
|
|
123
|
+
permissions.
|
|
124
|
+
- The current implementation supports send through `osascript` and optional
|
|
125
|
+
receive through `chat.db` polling when `receiveMode` is `chat-db`.
|
|
126
|
+
|
|
127
|
+
## Channel Adapter Contract
|
|
128
|
+
|
|
129
|
+
Each channel adapter must provide:
|
|
130
|
+
|
|
131
|
+
- `poll` or `receive` with a cursor and bounded batch size.
|
|
132
|
+
- `send` with normalized conversation ID and delivery result.
|
|
133
|
+
- `diagnose` for auth, permissions, and environment checks.
|
|
134
|
+
- Per-channel backoff hints for daemon runtime.
|
|
135
|
+
- Redaction rules for sensitive channel metadata and message contents.
|
|
136
|
+
|
|
137
|
+
## Agents
|
|
138
|
+
|
|
139
|
+
Codewith:
|
|
140
|
+
|
|
141
|
+
- Use a stable local session API or mailbox-backed background session.
|
|
142
|
+
- Use auth profiles explicitly, for example `account001`.
|
|
143
|
+
- Avoid typing into the interactive TUI as the primary integration method.
|
|
144
|
+
|
|
145
|
+
Claude Code:
|
|
146
|
+
|
|
147
|
+
- Use the official resumable/session surface where available.
|
|
148
|
+
- Keep account homes/profiles isolated.
|
|
149
|
+
- Mark any process-wrapper fallback clearly as compatibility mode and do not
|
|
150
|
+
advertise it as a durable session.
|
|
151
|
+
|
|
152
|
+
AIcopilot:
|
|
153
|
+
|
|
154
|
+
- Use the same session contract as Codewith where possible.
|
|
155
|
+
- Add create/send/events semantics if the repo does not expose them yet.
|
|
156
|
+
|
|
157
|
+
## Agent Adapter Contract
|
|
158
|
+
|
|
159
|
+
Each agent adapter must provide:
|
|
160
|
+
|
|
161
|
+
- `createSession(profile, cwd, options) -> agentSessionRef`.
|
|
162
|
+
- `sendMessage(sessionRef, text, options) -> final response or stream events`.
|
|
163
|
+
- `resumeSession(sessionRef)` after daemon restart.
|
|
164
|
+
- `cancel(sessionRef)` where the underlying agent supports cancellation.
|
|
165
|
+
- `close(sessionRef)` for local cleanup.
|
|
166
|
+
- Explicit timeout, output limit, stderr, and compatibility-mode behavior.
|
|
167
|
+
|
|
168
|
+
## Tracked Tasks
|
|
169
|
+
|
|
170
|
+
The local `todos` plan is `cba73b38-97c5-4a02-bf58-1a03069cc0c4`:
|
|
171
|
+
|
|
172
|
+
- `4e73f3bf-3fbb-4aae-8d01-045501db925d`: define durable bridge session model.
|
|
173
|
+
- `630b62e3-6d57-4121-b3de-90052ad21f66`: add session CLI and persistent bindings.
|
|
174
|
+
- `e5820e95-3c91-4523-a583-84bb4b1e7a56`: implement session-aware agent adapters.
|
|
175
|
+
- `56906077-0fdb-4767-bdd0-a1990508eade`: make Telegram work without prefixes.
|
|
176
|
+
- `a93e4741-529e-4718-a6c9-f8ed213756db`: add macOS iMessage channel adapter.
|
|
177
|
+
- `54698f24-7a62-4f02-9dba-4095194adc59`: release, reinstall, and verify bridge 0.2.
|
|
178
|
+
- `e268d9a7-ac40-4df9-9cfb-8e820795d87a`: adversarially verify the release.
|
|
179
|
+
|
|
180
|
+
## Done Criteria
|
|
181
|
+
|
|
182
|
+
- `bun run build`, `bun run typecheck`, and `bun test` pass.
|
|
183
|
+
- Session bindings survive daemon restarts.
|
|
184
|
+
- Telegram live test works with plain messages against the configured bot.
|
|
185
|
+
- iMessage diagnostics and send/receive smoke behavior work on macOS or fail
|
|
186
|
+
closed with a clear permission result.
|
|
187
|
+
- The package is committed, pushed, published, reinstalled locally, and smoke
|
|
188
|
+
tested from the public npm package.
|
|
189
|
+
- An adversarial review checks routing security, secret handling, daemon
|
|
190
|
+
lifecycle, replay/idempotency, channel spoofing, and package installability.
|
|
191
|
+
|
|
192
|
+
## MCP Migration
|
|
193
|
+
|
|
194
|
+
`bridge-mcp` must expose session-first tools:
|
|
195
|
+
|
|
196
|
+
- `bridge_session_list`
|
|
197
|
+
- `bridge_session_create`
|
|
198
|
+
- `bridge_session_attach`
|
|
199
|
+
- `bridge_session_send`
|
|
200
|
+
- `bridge_session_status`
|
|
201
|
+
- `bridge_session_route_message`
|
|
202
|
+
|
|
203
|
+
`bridge_route_message` can remain during `0.2.x` as compatibility-only and must
|
|
204
|
+
be documented that way in MCP metadata.
|