@qwen-code/channel-base 0.19.2 → 0.19.3-nightly.20260701.a974594d7
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 +100 -35
- package/dist/AcpBridge.d.ts +10 -16
- package/dist/AcpBridge.js +30 -1
- package/dist/AcpBridge.js.map +1 -1
- package/dist/BlockStreamer.d.ts +2 -0
- package/dist/BlockStreamer.js +5 -0
- package/dist/BlockStreamer.js.map +1 -1
- package/dist/ChannelAgentBridge.d.ts +47 -0
- package/dist/ChannelAgentBridge.js +2 -0
- package/dist/ChannelAgentBridge.js.map +1 -0
- package/dist/ChannelBase.d.ts +106 -5
- package/dist/ChannelBase.js +690 -48
- package/dist/ChannelBase.js.map +1 -1
- package/dist/DaemonChannelBridge.d.ts +2 -2
- package/dist/DaemonChannelBridge.js +16 -2
- package/dist/DaemonChannelBridge.js.map +1 -1
- package/dist/SessionRouter.d.ts +15 -13
- package/dist/SessionRouter.js +187 -44
- package/dist/SessionRouter.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/sanitize.d.ts +60 -0
- package/dist/sanitize.js +115 -0
- package/dist/sanitize.js.map +1 -0
- package/dist/types.d.ts +13 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @qwen-code/channel-base
|
|
2
2
|
|
|
3
|
-
Base infrastructure for building Qwen Code channel adapters. Provides the abstract base class, access control, session routing, and the
|
|
3
|
+
Base infrastructure for building Qwen Code channel adapters. Provides the abstract base class, access control, session routing, and the adapter-facing bridge interface used to communicate with the agent.
|
|
4
4
|
|
|
5
5
|
If you're building a channel plugin, this is your only dependency.
|
|
6
6
|
|
|
@@ -17,12 +17,22 @@ Subclass `ChannelBase` and implement three methods:
|
|
|
17
17
|
```typescript
|
|
18
18
|
import { ChannelBase } from '@qwen-code/channel-base';
|
|
19
19
|
import type {
|
|
20
|
+
ChannelAgentBridge,
|
|
21
|
+
ChannelBaseOptions,
|
|
20
22
|
ChannelConfig,
|
|
21
23
|
Envelope,
|
|
22
|
-
AcpBridge,
|
|
23
24
|
} from '@qwen-code/channel-base';
|
|
24
25
|
|
|
25
26
|
class MyChannel extends ChannelBase {
|
|
27
|
+
constructor(
|
|
28
|
+
name: string,
|
|
29
|
+
config: ChannelConfig,
|
|
30
|
+
bridge: ChannelAgentBridge,
|
|
31
|
+
options?: ChannelBaseOptions,
|
|
32
|
+
) {
|
|
33
|
+
super(name, config, bridge, options);
|
|
34
|
+
}
|
|
35
|
+
|
|
26
36
|
async connect(): Promise<void> {
|
|
27
37
|
// Connect to platform API, register message handlers.
|
|
28
38
|
// When a message arrives, build an Envelope and call:
|
|
@@ -55,6 +65,8 @@ export const plugin: ChannelPlugin = {
|
|
|
55
65
|
|
|
56
66
|
For a complete working example, see [`@qwen-code/channel-plugin-example`](../plugin-example/).
|
|
57
67
|
|
|
68
|
+
Migration note for existing TypeScript plugins: if your adapter constructor or factory explicitly types `bridge` as `AcpBridge`, change that annotation to `ChannelAgentBridge` and keep using only the methods exposed by that contract. JavaScript plugins are unaffected at runtime, and standalone `qwen channel start` still passes the current `AcpBridge` implementation.
|
|
69
|
+
|
|
58
70
|
## Architecture
|
|
59
71
|
|
|
60
72
|
```
|
|
@@ -63,9 +75,9 @@ Inbound: Platform message
|
|
|
63
75
|
→ GroupGate (group policy + mention gating)
|
|
64
76
|
→ SenderGate (allowlist / pairing / open)
|
|
65
77
|
→ Slash commands (/clear, /help, /status)
|
|
66
|
-
→ SessionRouter (resolve or create
|
|
78
|
+
→ SessionRouter (resolve or create agent session)
|
|
67
79
|
→ Resolve attachments (images → bridge, files → prompt text)
|
|
68
|
-
→
|
|
80
|
+
→ ChannelAgentBridge.prompt() → agent
|
|
69
81
|
|
|
70
82
|
Outbound: Agent response
|
|
71
83
|
→ BlockStreamer (if enabled: split into blocks at paragraph boundaries)
|
|
@@ -78,36 +90,39 @@ Everything between `handleInbound()` and `sendMessage()` is handled by the base
|
|
|
78
90
|
|
|
79
91
|
### Classes
|
|
80
92
|
|
|
81
|
-
| Class | Purpose
|
|
82
|
-
| --------------- |
|
|
83
|
-
| `ChannelBase` | Abstract base class — extend this to build a channel adapter
|
|
84
|
-
| `AcpBridge` |
|
|
85
|
-
| `BlockStreamer` | Progressive multi-message delivery for block streaming
|
|
86
|
-
| `SessionRouter` | Maps senders to
|
|
87
|
-
| `SenderGate` | DM access control (allowlist / pairing / open)
|
|
88
|
-
| `GroupGate` | Group chat policy and @mention gating
|
|
89
|
-
| `PairingStore` | Pairing code generation, approval, and allowlist persistence
|
|
93
|
+
| Class | Purpose |
|
|
94
|
+
| --------------- | ------------------------------------------------------------------------------------ |
|
|
95
|
+
| `ChannelBase` | Abstract base class — extend this to build a channel adapter |
|
|
96
|
+
| `AcpBridge` | Current standalone `qwen channel start` bridge implementation over `qwen-code --acp` |
|
|
97
|
+
| `BlockStreamer` | Progressive multi-message delivery for block streaming |
|
|
98
|
+
| `SessionRouter` | Maps senders to agent sessions with configurable scoping |
|
|
99
|
+
| `SenderGate` | DM access control (allowlist / pairing / open) |
|
|
100
|
+
| `GroupGate` | Group chat policy and @mention gating |
|
|
101
|
+
| `PairingStore` | Pairing code generation, approval, and allowlist persistence |
|
|
90
102
|
|
|
91
103
|
### Types
|
|
92
104
|
|
|
93
|
-
| Type
|
|
94
|
-
|
|
|
95
|
-
| `Attachment`
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
105
|
+
| Type | Description |
|
|
106
|
+
| -------------------- | ------------------------------------------------------------------------ |
|
|
107
|
+
| `Attachment` | Structured file/image/audio/video attachment |
|
|
108
|
+
| `AvailableCommand` | Agent command advertised through the bridge |
|
|
109
|
+
| `ChannelAgentBridge` | Adapter-facing bridge contract used by `ChannelBase` and `SessionRouter` |
|
|
110
|
+
| `ChannelConfig` | Channel configuration from `settings.json` |
|
|
111
|
+
| `ChannelPlugin` | Plugin factory interface (what you export) |
|
|
112
|
+
| `Envelope` | Normalized inbound message format |
|
|
113
|
+
| `SenderPolicy` | `'allowlist' \| 'pairing' \| 'open'` |
|
|
114
|
+
| `GroupPolicy` | `'disabled' \| 'allowlist' \| 'open'` |
|
|
115
|
+
| `SessionScope` | `'user' \| 'thread' \| 'single'` |
|
|
116
|
+
| `GroupConfig` | Per-group settings (e.g. `requireMention`) |
|
|
117
|
+
| `SessionTarget` | Maps a session back to its channel/sender/chat |
|
|
118
|
+
| `ToolCallEvent` | Agent tool-call event delivered to adapters |
|
|
104
119
|
|
|
105
120
|
## API reference
|
|
106
121
|
|
|
107
122
|
### ChannelBase
|
|
108
123
|
|
|
109
124
|
```typescript
|
|
110
|
-
constructor(name: string, config: ChannelConfig, bridge:
|
|
125
|
+
constructor(name: string, config: ChannelConfig, bridge: ChannelAgentBridge, options?: ChannelBaseOptions)
|
|
111
126
|
```
|
|
112
127
|
|
|
113
128
|
**Abstract methods** (you must implement):
|
|
@@ -123,7 +138,7 @@ constructor(name: string, config: ChannelConfig, bridge: AcpBridge, options?: Ch
|
|
|
123
138
|
| Method | Description |
|
|
124
139
|
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
125
140
|
| `handleInbound(envelope)` | Route an inbound message through the full pipeline (gate checks, commands, session, prompt). Call this from your message handler. |
|
|
126
|
-
| `setBridge(bridge)` | Replace the
|
|
141
|
+
| `setBridge(bridge)` | Replace the agent bridge after crash recovery |
|
|
127
142
|
| `registerCommand(name, handler)` | Register a custom slash command (e.g. `/mycommand`) |
|
|
128
143
|
| `onToolCall(chatId, event)` | Hook called on agent tool invocations — override to show indicators |
|
|
129
144
|
| `onResponseChunk(chatId, chunk, sessionId)` | Hook called per streaming text chunk — override for progressive display (default: no-op) |
|
|
@@ -133,9 +148,58 @@ constructor(name: string, config: ChannelConfig, bridge: AcpBridge, options?: Ch
|
|
|
133
148
|
|
|
134
149
|
**Built-in slash commands:** `/clear` (`/reset`, `/new`), `/help`, `/status`
|
|
135
150
|
|
|
151
|
+
**ChannelBaseOptions:**
|
|
152
|
+
|
|
153
|
+
| Option | Description |
|
|
154
|
+
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
155
|
+
| `router` | Optional `SessionRouter`. Omit for the default standalone router. |
|
|
156
|
+
| `proxy` | Optional proxy URL made available to adapters. |
|
|
157
|
+
| `registerBridgeEvents` | Set only when the adapter owns a supplied router and `ChannelBase` should consume bridge events directly. Leave unset for routers supplied by the gateway. |
|
|
158
|
+
|
|
159
|
+
### ChannelAgentBridge
|
|
160
|
+
|
|
161
|
+
`ChannelAgentBridge` is the adapter-facing contract. Channel adapters, channel plugins, `ChannelBase`, and `SessionRouter` should depend on this type instead of a concrete bridge implementation.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
interface ChannelAgentBridge {
|
|
165
|
+
readonly availableCommands: AvailableCommand[];
|
|
166
|
+
on(eventName: 'toolCall', listener: (event: ToolCallEvent) => void): unknown;
|
|
167
|
+
on(
|
|
168
|
+
eventName: 'textChunk',
|
|
169
|
+
listener: (sessionId: string, chunk: string) => void,
|
|
170
|
+
): unknown;
|
|
171
|
+
on(
|
|
172
|
+
eventName: 'sessionDied',
|
|
173
|
+
listener: (event: { sessionId: string; reason?: string }) => void,
|
|
174
|
+
): unknown;
|
|
175
|
+
off(eventName: 'toolCall', listener: (event: ToolCallEvent) => void): unknown;
|
|
176
|
+
off(
|
|
177
|
+
eventName: 'textChunk',
|
|
178
|
+
listener: (sessionId: string, chunk: string) => void,
|
|
179
|
+
): unknown;
|
|
180
|
+
off(
|
|
181
|
+
eventName: 'sessionDied',
|
|
182
|
+
listener: (event: { sessionId: string; reason?: string }) => void,
|
|
183
|
+
): unknown;
|
|
184
|
+
newSession(cwd: string): Promise<string>;
|
|
185
|
+
loadSession(sessionId: string, cwd: string): Promise<string>;
|
|
186
|
+
prompt(
|
|
187
|
+
sessionId: string,
|
|
188
|
+
text: string,
|
|
189
|
+
options?: { imageBase64?: string; imageMimeType?: string },
|
|
190
|
+
): Promise<string>;
|
|
191
|
+
cancelSession(sessionId: string): Promise<void>;
|
|
192
|
+
shellCommand?(
|
|
193
|
+
sessionId: string,
|
|
194
|
+
command: string,
|
|
195
|
+
signal?: AbortSignal,
|
|
196
|
+
): Promise<{ exitCode: number | null; output: string; aborted: boolean }>;
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
136
200
|
### AcpBridge
|
|
137
201
|
|
|
138
|
-
|
|
202
|
+
`AcpBridge` is the current implementation used by standalone `qwen channel start`. It manages the `qwen-code --acp` child process and implements `ChannelAgentBridge`.
|
|
139
203
|
|
|
140
204
|
```typescript
|
|
141
205
|
constructor(options: { cliEntryPath: string; cwd: string; model?: string })
|
|
@@ -160,10 +224,10 @@ constructor(options: { cliEntryPath: string; cwd: string; model?: string })
|
|
|
160
224
|
|
|
161
225
|
### SessionRouter
|
|
162
226
|
|
|
163
|
-
Maps senders to
|
|
227
|
+
Maps senders to agent sessions based on the configured scope.
|
|
164
228
|
|
|
165
229
|
```typescript
|
|
166
|
-
constructor(bridge:
|
|
230
|
+
constructor(bridge: ChannelAgentBridge, defaultCwd: string, scope?: SessionScope, persistPath?: string)
|
|
167
231
|
```
|
|
168
232
|
|
|
169
233
|
**Routing keys by scope:**
|
|
@@ -174,12 +238,13 @@ constructor(bridge: AcpBridge, defaultCwd: string, scope?: SessionScope, persist
|
|
|
174
238
|
| `thread` | `channel:threadId` | One session per thread |
|
|
175
239
|
| `single` | `channel:__single__` | One shared session for the entire channel |
|
|
176
240
|
|
|
177
|
-
| Method
|
|
178
|
-
|
|
|
179
|
-
| `resolve(channelName, senderId, chatId, threadId?, cwd?)`
|
|
180
|
-
| `removeSession(channelName, senderId, chatId?)`
|
|
181
|
-
| `
|
|
182
|
-
| `
|
|
241
|
+
| Method | Description |
|
|
242
|
+
| ---------------------------------------------------------- | ----------------------------------------------------------- |
|
|
243
|
+
| `resolve(channelName, senderId, chatId, threadId?, cwd?)` | Get or create a session for the given sender |
|
|
244
|
+
| `removeSession(channelName, senderId, chatId?, threadId?)` | Remove session(s) — used by `/clear` |
|
|
245
|
+
| `removeSessionId(sessionId)` | Remove all routing state for a bridge session ID |
|
|
246
|
+
| `restoreSessions()` | Reload sessions from disk after bridge restart |
|
|
247
|
+
| `clearAll()` | Clear all sessions and delete persist file (clean shutdown) |
|
|
183
248
|
|
|
184
249
|
### SenderGate
|
|
185
250
|
|
package/dist/AcpBridge.d.ts
CHANGED
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { AvailableCommand, ChannelAgentBridge } from './ChannelAgentBridge.js';
|
|
3
|
+
export type { AvailableCommand, ToolCallEvent } from './ChannelAgentBridge.js';
|
|
2
4
|
export interface AcpBridgeOptions {
|
|
3
5
|
cliEntryPath: string;
|
|
4
6
|
cwd: string;
|
|
5
7
|
model?: string;
|
|
6
8
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export
|
|
15
|
-
sessionId: string;
|
|
16
|
-
toolCallId: string;
|
|
17
|
-
kind: string;
|
|
18
|
-
title: string;
|
|
19
|
-
status: string;
|
|
20
|
-
rawInput?: Record<string, unknown>;
|
|
21
|
-
}
|
|
22
|
-
export declare class AcpBridge extends EventEmitter {
|
|
9
|
+
/**
|
|
10
|
+
* Read a command's aliases off a raw wire `available_commands_update` entry. ACP
|
|
11
|
+
* carries them in `_meta` (its only extension point); a top-level `altNames` is
|
|
12
|
+
* also accepted for forward-compat. Returns undefined when absent so the field
|
|
13
|
+
* stays optional and entries without aliases are left byte-identical.
|
|
14
|
+
*/
|
|
15
|
+
export declare function readAvailableCommandAltNames(raw: unknown): string[] | undefined;
|
|
16
|
+
export declare class AcpBridge extends EventEmitter implements ChannelAgentBridge {
|
|
23
17
|
private child;
|
|
24
18
|
private connection;
|
|
25
19
|
private options;
|
package/dist/AcpBridge.js
CHANGED
|
@@ -2,6 +2,30 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { Readable, Writable } from 'node:stream';
|
|
3
3
|
import { EventEmitter } from 'node:events';
|
|
4
4
|
import { ClientSideConnection, ndJsonStream, PROTOCOL_VERSION, } from '@agentclientprotocol/sdk';
|
|
5
|
+
/**
|
|
6
|
+
* Read a command's aliases off a raw wire `available_commands_update` entry. ACP
|
|
7
|
+
* carries them in `_meta` (its only extension point); a top-level `altNames` is
|
|
8
|
+
* also accepted for forward-compat. Returns undefined when absent so the field
|
|
9
|
+
* stays optional and entries without aliases are left byte-identical.
|
|
10
|
+
*/
|
|
11
|
+
export function readAvailableCommandAltNames(raw) {
|
|
12
|
+
if (typeof raw !== 'object' || raw === null)
|
|
13
|
+
return undefined;
|
|
14
|
+
const record = raw;
|
|
15
|
+
const meta = record['_meta'];
|
|
16
|
+
const fromMeta = typeof meta === 'object' && meta !== null
|
|
17
|
+
? meta['altNames']
|
|
18
|
+
: undefined;
|
|
19
|
+
const source = Array.isArray(record['altNames'])
|
|
20
|
+
? record['altNames']
|
|
21
|
+
: Array.isArray(fromMeta)
|
|
22
|
+
? fromMeta
|
|
23
|
+
: undefined;
|
|
24
|
+
if (!source)
|
|
25
|
+
return undefined;
|
|
26
|
+
const names = source.filter((n) => typeof n === 'string');
|
|
27
|
+
return names.length > 0 ? names : undefined;
|
|
28
|
+
}
|
|
5
29
|
export class AcpBridge extends EventEmitter {
|
|
6
30
|
child = null;
|
|
7
31
|
connection = null;
|
|
@@ -38,6 +62,8 @@ export class AcpBridge extends EventEmitter {
|
|
|
38
62
|
});
|
|
39
63
|
this.child.on('exit', (code, signal) => {
|
|
40
64
|
process.stderr.write(`[AcpBridge] Process exited (code=${code}, signal=${signal})\n`);
|
|
65
|
+
// Do not emit sessionDied here: a full ACP process exit is handled by
|
|
66
|
+
// channel start crash recovery, which reloads the persisted sessions.
|
|
41
67
|
this.connection = null;
|
|
42
68
|
this.child = null;
|
|
43
69
|
this.emit('disconnected', code, signal);
|
|
@@ -154,7 +180,10 @@ export class AcpBridge extends EventEmitter {
|
|
|
154
180
|
}
|
|
155
181
|
case 'available_commands_update': {
|
|
156
182
|
if (Array.isArray(update['availableCommands'])) {
|
|
157
|
-
this._availableCommands = update['availableCommands']
|
|
183
|
+
this._availableCommands = update['availableCommands'].map((cmd) => {
|
|
184
|
+
const altNames = readAvailableCommandAltNames(cmd);
|
|
185
|
+
return altNames ? { ...cmd, altNames } : cmd;
|
|
186
|
+
});
|
|
158
187
|
}
|
|
159
188
|
break;
|
|
160
189
|
}
|
package/dist/AcpBridge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AcpBridge.js","sourceRoot":"","sources":["../src/AcpBridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"AcpBridge.js","sourceRoot":"","sources":["../src/AcpBridge.ts"],"names":[],"mappings":"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"}
|
package/dist/BlockStreamer.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export declare class BlockStreamer {
|
|
|
36
36
|
push(chunk: string): void;
|
|
37
37
|
/** Flush all remaining buffered text. Awaits all pending sends. */
|
|
38
38
|
flush(): Promise<void>;
|
|
39
|
+
/** Drop buffered text and cancel future idle emission. */
|
|
40
|
+
stop(): void;
|
|
39
41
|
private checkEmit;
|
|
40
42
|
private onIdle;
|
|
41
43
|
private emitBlock;
|
package/dist/BlockStreamer.js
CHANGED
|
@@ -42,6 +42,11 @@ export class BlockStreamer {
|
|
|
42
42
|
}
|
|
43
43
|
await this.sending;
|
|
44
44
|
}
|
|
45
|
+
/** Drop buffered text and cancel future idle emission. */
|
|
46
|
+
stop() {
|
|
47
|
+
this.clearIdleTimer();
|
|
48
|
+
this.buffer = '';
|
|
49
|
+
}
|
|
45
50
|
// ---------------------------------------------------------------------------
|
|
46
51
|
// Internal
|
|
47
52
|
// ---------------------------------------------------------------------------
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlockStreamer.js","sourceRoot":"","sources":["../src/BlockStreamer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAaH,MAAM,OAAO,aAAa;IAChB,MAAM,GAAG,EAAE,CAAC;IACZ,SAAS,GAAyC,IAAI,CAAC;IACvD,OAAO,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3C,IAAI,CAAuB;IAEnC,uCAAuC;IACvC,UAAU,GAAG,CAAC,CAAC;IAEf,YAAY,IAA0B;QACpC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,mDAAmD;IACnD,IAAI,CAAC,KAAa;QAChB,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC;QACrB,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,SAAS,EAAE,CAAC;QAEjB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnD,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5B,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACnB,CAAC;QACD,MAAM,IAAI,CAAC,OAAO,CAAC;IACrB,CAAC;IAED,8EAA8E;IAC9E,WAAW;IACX,8EAA8E;IAEtE,SAAS;QACf,4CAA4C;QAC5C,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChD,MAAM,EAAE,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAChE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YACzC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACtC,CAAC;QAED,uDAAuD;QACvD,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC7C,MAAM,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC/C,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;gBACX,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;gBACzC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;IACH,CAAC;IAEO,MAAM;QACZ,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC7C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5B,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;IAEO,SAAS,CAAC,IAAY;QAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO;aACxB,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;aACnC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACrB,CAAC;IAED;;;;OAIG;IACK,iBAAiB,CAAC,IAAY;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO,CAAC,CAAC,CAAC;QACrD,OAAO,IAAI,GAAG,CAAC,CAAC;IAClB,CAAC;IAED;;;OAGG;IACK,cAAc,CAAC,IAAY,EAAE,MAAc;QACjD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAClC,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACrC,IAAI,IAAI,GAAG,CAAC;YAAE,OAAO,IAAI,GAAG,CAAC,CAAC;QAC9B,MAAM,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,EAAE,GAAG,CAAC;YAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QAC1B,MAAM,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,EAAE,GAAG,CAAC;YAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QAC1B,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,cAAc;QACpB,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC5B,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;IACH,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"BlockStreamer.js","sourceRoot":"","sources":["../src/BlockStreamer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface AvailableCommand {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
input?: {
|
|
5
|
+
hint: string;
|
|
6
|
+
} | null;
|
|
7
|
+
/**
|
|
8
|
+
* Aliases the agent's parser also accepts for this command (for example
|
|
9
|
+
* `summarize` for `compress`).
|
|
10
|
+
*/
|
|
11
|
+
altNames?: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface ToolCallEvent {
|
|
14
|
+
sessionId: string;
|
|
15
|
+
toolCallId: string;
|
|
16
|
+
kind: string;
|
|
17
|
+
title: string;
|
|
18
|
+
status: string;
|
|
19
|
+
rawInput?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
export interface SessionDiedEvent {
|
|
22
|
+
sessionId: string;
|
|
23
|
+
reason?: string;
|
|
24
|
+
}
|
|
25
|
+
interface ChannelAgentBridgeEventMap {
|
|
26
|
+
sessionDied: [SessionDiedEvent];
|
|
27
|
+
textChunk: [sessionId: string, chunk: string];
|
|
28
|
+
toolCall: [ToolCallEvent];
|
|
29
|
+
}
|
|
30
|
+
export interface ChannelAgentBridge {
|
|
31
|
+
readonly availableCommands: AvailableCommand[];
|
|
32
|
+
on<K extends keyof ChannelAgentBridgeEventMap>(eventName: K, listener: (...args: ChannelAgentBridgeEventMap[K]) => void): unknown;
|
|
33
|
+
off<K extends keyof ChannelAgentBridgeEventMap>(eventName: K, listener: (...args: ChannelAgentBridgeEventMap[K]) => void): unknown;
|
|
34
|
+
newSession(cwd: string): Promise<string>;
|
|
35
|
+
loadSession(sessionId: string, cwd: string): Promise<string>;
|
|
36
|
+
prompt(sessionId: string, text: string, options?: {
|
|
37
|
+
imageBase64?: string;
|
|
38
|
+
imageMimeType?: string;
|
|
39
|
+
}): Promise<string>;
|
|
40
|
+
cancelSession(sessionId: string): Promise<void>;
|
|
41
|
+
shellCommand?(sessionId: string, command: string, signal?: AbortSignal): Promise<{
|
|
42
|
+
exitCode: number | null;
|
|
43
|
+
output: string;
|
|
44
|
+
aborted: boolean;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChannelAgentBridge.js","sourceRoot":"","sources":["../src/ChannelAgentBridge.ts"],"names":[],"mappings":""}
|
package/dist/ChannelBase.d.ts
CHANGED
|
@@ -2,16 +2,30 @@ import type { ChannelConfig, Envelope } from './types.js';
|
|
|
2
2
|
import { GroupGate } from './GroupGate.js';
|
|
3
3
|
import { SenderGate } from './SenderGate.js';
|
|
4
4
|
import { SessionRouter } from './SessionRouter.js';
|
|
5
|
-
import type {
|
|
5
|
+
import type { ChannelAgentBridge, ToolCallEvent } from './ChannelAgentBridge.js';
|
|
6
|
+
/**
|
|
7
|
+
* Max time /clear waits for a cancelled in-flight turn to wind down before
|
|
8
|
+
* purging anyway. A wedged ACP child (stuck tool call, not reading stdin, or
|
|
9
|
+
* crashed without closing) can leave active.done unresolved forever; without
|
|
10
|
+
* this bound /clear — and the whole channel — would hang. Safe because the
|
|
11
|
+
* purge runs regardless and the generation is bumped, so a turn that settles
|
|
12
|
+
* later is already invalidated.
|
|
13
|
+
*/
|
|
14
|
+
export declare const CLEAR_CANCEL_TIMEOUT_MS = 3000;
|
|
6
15
|
export interface ChannelBaseOptions {
|
|
7
16
|
router?: SessionRouter;
|
|
8
17
|
proxy?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Set when a channel owns a supplied router and should consume bridge
|
|
20
|
+
* events directly.
|
|
21
|
+
*/
|
|
22
|
+
registerBridgeEvents?: boolean;
|
|
9
23
|
}
|
|
10
24
|
/** Handler for a slash command. Return true if handled, false to forward to agent. */
|
|
11
25
|
type CommandHandler = (envelope: Envelope, args: string) => Promise<boolean>;
|
|
12
26
|
export declare abstract class ChannelBase {
|
|
13
27
|
protected config: ChannelConfig;
|
|
14
|
-
protected bridge:
|
|
28
|
+
protected bridge: ChannelAgentBridge;
|
|
15
29
|
protected groupGate: GroupGate;
|
|
16
30
|
protected gate: SenderGate;
|
|
17
31
|
protected router: SessionRouter;
|
|
@@ -22,17 +36,29 @@ export declare abstract class ChannelBase {
|
|
|
22
36
|
private commands;
|
|
23
37
|
/** Per-session promise chain to serialize prompt + send (followup mode). */
|
|
24
38
|
private sessionQueues;
|
|
39
|
+
private readonly registerBridgeEvents;
|
|
40
|
+
/**
|
|
41
|
+
* Per-session generation, bumped by /clear. A queued followup turn captures the
|
|
42
|
+
* generation when it enqueues and bails if /clear bumped it before the turn ran,
|
|
43
|
+
* so a cleared session can't be resurrected by an already-queued prompt.
|
|
44
|
+
*/
|
|
45
|
+
private sessionGenerations;
|
|
25
46
|
/** Per-session active prompt tracking for dispatch modes. */
|
|
26
47
|
private activePrompts;
|
|
27
48
|
/** Per-session message buffer for collect mode. */
|
|
28
49
|
private collectBuffers;
|
|
29
|
-
|
|
50
|
+
private readonly bridgeToolCallListener;
|
|
51
|
+
private readonly bridgeSessionDiedListener;
|
|
52
|
+
constructor(name: string, config: ChannelConfig, bridge: ChannelAgentBridge, options?: ChannelBaseOptions);
|
|
30
53
|
abstract connect(): Promise<void>;
|
|
31
54
|
abstract sendMessage(chatId: string, text: string): Promise<void>;
|
|
32
55
|
abstract disconnect(): void;
|
|
33
56
|
/** Replace the bridge instance (used after crash recovery restart). */
|
|
34
|
-
setBridge(bridge:
|
|
57
|
+
setBridge(bridge: ChannelAgentBridge): void;
|
|
35
58
|
onToolCall(_chatId: string, _event: ToolCallEvent): void;
|
|
59
|
+
onSessionDied(sessionId: string): void;
|
|
60
|
+
private attachBridgeEvents;
|
|
61
|
+
private detachBridgeEvents;
|
|
36
62
|
/**
|
|
37
63
|
* Called when a prompt actually begins processing (inside the session queue).
|
|
38
64
|
* Override to show a platform-specific working indicator (e.g., typing, reaction).
|
|
@@ -62,15 +88,90 @@ export declare abstract class ChannelBase {
|
|
|
62
88
|
* Overrides shared commands if the same name is registered.
|
|
63
89
|
*/
|
|
64
90
|
protected registerCommand(name: string, handler: CommandHandler): void;
|
|
91
|
+
protected registerCancelCommand(name?: string): void;
|
|
65
92
|
/** Register shared slash commands. Called from constructor. */
|
|
66
93
|
private registerSharedCommands;
|
|
67
94
|
/** Check if a message text matches a registered local command. */
|
|
68
95
|
protected isLocalCommand(text: string): boolean;
|
|
96
|
+
private findActiveSessionId;
|
|
97
|
+
/**
|
|
98
|
+
* Whether the resolved session is SHARED across senders. `single` collapses
|
|
99
|
+
* the whole channel to one `__single__` session for EVERY sender — group OR
|
|
100
|
+
* DM — so it is ALWAYS shared (even a DM maps to `__single__`). `thread` is
|
|
101
|
+
* shared only in a group (a DM maps to the lone caller's own chat). `user` is
|
|
102
|
+
* per-sender, never shared. Drives both the destructive-/clear confirm gate
|
|
103
|
+
* and the host-shell (`!`) gate.
|
|
104
|
+
*/
|
|
105
|
+
private isSharedSession;
|
|
106
|
+
/**
|
|
107
|
+
* Whether `envelope.senderId` may act on the resolved session's destructive or
|
|
108
|
+
* workspace-leaking commands (/clear, /who). A SHARED session with a non-empty
|
|
109
|
+
* allowedUsers list is restricted to those members; a per-user session, or one
|
|
110
|
+
* with no allowlist, is unrestricted. Shared verbatim by /clear and /who so the
|
|
111
|
+
* gate can't drift; each caller sends its own rejection wording.
|
|
112
|
+
*/
|
|
113
|
+
private isAuthorizedForSharedSession;
|
|
114
|
+
private stopActiveStreaming;
|
|
115
|
+
/**
|
|
116
|
+
* Cancel the active turn and wait (bounded) for it to wind down. Stops the
|
|
117
|
+
* BlockStreamer so buffered text can't leak via the idle timer, then fires a
|
|
118
|
+
* best-effort cancelSession (NOT awaited — a wedged child/daemon can leave the
|
|
119
|
+
* request pending forever). Returns true if active.done settled first, false
|
|
120
|
+
* if the CLEAR_CANCEL_TIMEOUT_MS bound won (the turn never wound down). Used by
|
|
121
|
+
* /clear, which genuinely EVICTS the session and so must proceed even when the
|
|
122
|
+
* turn is wedged. Steer no longer uses this: it best-effort cancels then chains
|
|
123
|
+
* the new turn behind the old one (see handleInbound), so it never needs to
|
|
124
|
+
* proceed past a still-active turn.
|
|
125
|
+
*/
|
|
126
|
+
private cancelAndAwaitActive;
|
|
69
127
|
/**
|
|
70
128
|
* Parse a slash command from message text.
|
|
71
|
-
* Returns { command, args } or null if not a slash command.
|
|
129
|
+
* Returns { command, raw, args } or null if not a slash command. `command` is
|
|
130
|
+
* lowercased for case-insensitive LOCAL dispatch (registerCommand lowercases the
|
|
131
|
+
* names it stores); `raw` keeps the typed case so agent-command matching can be
|
|
132
|
+
* CASE-SENSITIVE, mirroring the CLI's parseSlashCommand (`cmd.name === part`).
|
|
72
133
|
*/
|
|
73
134
|
private parseCommand;
|
|
135
|
+
/**
|
|
136
|
+
* Whether `text` is a real slash command rather than prose that merely starts
|
|
137
|
+
* with `/`. A command's first whitespace-delimited token must match
|
|
138
|
+
* parseCommand()'s charset — `[a-zA-Z0-9_:-]+`, plus an optional `@botname`
|
|
139
|
+
* suffix — and not be a `//` line comment or `/*` block comment. Slash-prefixed
|
|
140
|
+
* paths (`/tmp/foo`), comments, and a bare `/` are prose and keep their
|
|
141
|
+
* `[sender]` tag.
|
|
142
|
+
*
|
|
143
|
+
* Intentionally stricter than the CLI's looser classifier (cli
|
|
144
|
+
* `ui/utils/commandUtils.ts`), which forwards any non-comment, non-path
|
|
145
|
+
* `/<token>` (e.g. `/café`, a zero-width-laden token). Such inputs aren't
|
|
146
|
+
* runnable commands, and in a SHARED group session forwarding them unattributed
|
|
147
|
+
* is worse than a redundant tag — so anything off the command charset is
|
|
148
|
+
* treated as prose and keeps its `[sender]` tag. Purely lexical — never
|
|
149
|
+
* consults the async command list, so it can't race a fresh session.
|
|
150
|
+
*/
|
|
151
|
+
private isSlashCommand;
|
|
152
|
+
/**
|
|
153
|
+
* Whether `text` names a command this channel can actually run: a locally
|
|
154
|
+
* registered command (`this.commands`, e.g. /clear, /who) OR an agent command
|
|
155
|
+
* THIS session exposes — by canonical name OR alias (e.g. `/summarize` for
|
|
156
|
+
* `/compress`). Paired with isSlashCommand so the `[sender]` attribution tag is
|
|
157
|
+
* suppressed ONLY for RECOGNIZED commands; command-SHAPED-but-unrecognized text
|
|
158
|
+
* (e.g. `/x\n[SYSTEM]: …`) keeps its tag rather than reaching a shared group
|
|
159
|
+
* unattributed, where an injected second line is more likely read as a system
|
|
160
|
+
* directive. Purely synchronous, like isSlashCommand: it reads the session's
|
|
161
|
+
* availableCommands snapshot WITHOUT awaiting, so it never races a fresh session
|
|
162
|
+
* (a genuine agent command sent before the snapshot loads is treated as
|
|
163
|
+
* unrecognized and KEEPS its tag — the safe default).
|
|
164
|
+
*/
|
|
165
|
+
private isRecognizedCommand;
|
|
166
|
+
/**
|
|
167
|
+
* The agent-command snapshot for THIS session. DaemonChannelBridge keys
|
|
168
|
+
* commands per session, so its global `availableCommands` getter can return
|
|
169
|
+
* ANOTHER session's list — prefer its getAvailableCommands(sessionId) when
|
|
170
|
+
* present. AcpBridge runs a single agent and exposes only the global getter
|
|
171
|
+
* (inherently session-correct), so fall back to it. Synchronous, matching
|
|
172
|
+
* isRecognizedCommand's no-await contract.
|
|
173
|
+
*/
|
|
174
|
+
private getAgentCommandsForSession;
|
|
74
175
|
handleInbound(envelope: Envelope): Promise<void>;
|
|
75
176
|
protected onPairingRequired(chatId: string, code: string | null): Promise<void>;
|
|
76
177
|
}
|