@qwen-code/channel-base 0.19.3 → 0.19.4

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 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 ACP bridge that communicates with the agent.
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,17 @@ 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
+
70
+ ## Runtime modes
71
+
72
+ Channel adapters can run in two host modes:
73
+
74
+ - `qwen channel start [name]` is the standalone service. It uses `AcpBridge` over a `qwen-code --acp` child process and remains the default channel command.
75
+ - `qwen serve --channel <name>` and `qwen serve --channel all` are experimental daemon-managed modes. `qwen serve` starts one out-of-process channel worker, the worker connects back to the daemon through the SDK, and adapters receive a `DaemonChannelBridge`-backed `ChannelAgentBridge` facade.
76
+
77
+ In daemon-managed mode, one daemon is bound to one workspace. Every selected channel's `cwd` must resolve to that same workspace. The optional `shellCommand` method is exposed to adapters only when the daemon advertises the `session_shell_command` capability.
78
+
58
79
  ## Architecture
59
80
 
60
81
  ```
@@ -63,9 +84,9 @@ Inbound: Platform message
63
84
  → GroupGate (group policy + mention gating)
64
85
  → SenderGate (allowlist / pairing / open)
65
86
  → Slash commands (/clear, /help, /status)
66
- → SessionRouter (resolve or create ACP session)
87
+ → SessionRouter (resolve or create agent session)
67
88
  → Resolve attachments (images → bridge, files → prompt text)
68
- → AcpBridge.prompt() → agent
89
+ → ChannelAgentBridge.prompt() → agent
69
90
 
70
91
  Outbound: Agent response
71
92
  → BlockStreamer (if enabled: split into blocks at paragraph boundaries)
@@ -78,36 +99,39 @@ Everything between `handleInbound()` and `sendMessage()` is handled by the base
78
99
 
79
100
  ### Classes
80
101
 
81
- | Class | Purpose |
82
- | --------------- | ---------------------------------------------------------------- |
83
- | `ChannelBase` | Abstract base class — extend this to build a channel adapter |
84
- | `AcpBridge` | Spawns and communicates with the `qwen-code --acp` agent process |
85
- | `BlockStreamer` | Progressive multi-message delivery for block streaming |
86
- | `SessionRouter` | Maps senders to ACP sessions with configurable scoping |
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 |
102
+ | Class | Purpose |
103
+ | --------------- | ------------------------------------------------------------------------------------ |
104
+ | `ChannelBase` | Abstract base class — extend this to build a channel adapter |
105
+ | `AcpBridge` | Current standalone `qwen channel start` bridge implementation over `qwen-code --acp` |
106
+ | `BlockStreamer` | Progressive multi-message delivery for block streaming |
107
+ | `SessionRouter` | Maps senders to agent sessions with configurable scoping |
108
+ | `SenderGate` | DM access control (allowlist / pairing / open) |
109
+ | `GroupGate` | Group chat policy and @mention gating |
110
+ | `PairingStore` | Pairing code generation, approval, and allowlist persistence |
90
111
 
91
112
  ### Types
92
113
 
93
- | Type | Description |
94
- | --------------- | ---------------------------------------------- |
95
- | `Attachment` | Structured file/image/audio/video attachment |
96
- | `ChannelConfig` | Channel configuration from `settings.json` |
97
- | `ChannelPlugin` | Plugin factory interface (what you export) |
98
- | `Envelope` | Normalized inbound message format |
99
- | `SenderPolicy` | `'allowlist' \| 'pairing' \| 'open'` |
100
- | `GroupPolicy` | `'disabled' \| 'allowlist' \| 'open'` |
101
- | `SessionScope` | `'user' \| 'thread' \| 'single'` |
102
- | `GroupConfig` | Per-group settings (e.g. `requireMention`) |
103
- | `SessionTarget` | Maps a session back to its channel/sender/chat |
114
+ | Type | Description |
115
+ | -------------------- | ------------------------------------------------------------------------ |
116
+ | `Attachment` | Structured file/image/audio/video attachment |
117
+ | `AvailableCommand` | Agent command advertised through the bridge |
118
+ | `ChannelAgentBridge` | Adapter-facing bridge contract used by `ChannelBase` and `SessionRouter` |
119
+ | `ChannelConfig` | Channel configuration from `settings.json` |
120
+ | `ChannelPlugin` | Plugin factory interface (what you export) |
121
+ | `Envelope` | Normalized inbound message format |
122
+ | `SenderPolicy` | `'allowlist' \| 'pairing' \| 'open'` |
123
+ | `GroupPolicy` | `'disabled' \| 'allowlist' \| 'open'` |
124
+ | `SessionScope` | `'user' \| 'thread' \| 'single'` |
125
+ | `GroupConfig` | Per-group settings (e.g. `requireMention`) |
126
+ | `SessionTarget` | Maps a session back to its channel/sender/chat |
127
+ | `ToolCallEvent` | Agent tool-call event delivered to adapters |
104
128
 
105
129
  ## API reference
106
130
 
107
131
  ### ChannelBase
108
132
 
109
133
  ```typescript
110
- constructor(name: string, config: ChannelConfig, bridge: AcpBridge, options?: ChannelBaseOptions)
134
+ constructor(name: string, config: ChannelConfig, bridge: ChannelAgentBridge, options?: ChannelBaseOptions)
111
135
  ```
112
136
 
113
137
  **Abstract methods** (you must implement):
@@ -123,7 +147,7 @@ constructor(name: string, config: ChannelConfig, bridge: AcpBridge, options?: Ch
123
147
  | Method | Description |
124
148
  | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
125
149
  | `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 ACP bridge after crash recovery |
150
+ | `setBridge(bridge)` | Replace the agent bridge after crash recovery |
127
151
  | `registerCommand(name, handler)` | Register a custom slash command (e.g. `/mycommand`) |
128
152
  | `onToolCall(chatId, event)` | Hook called on agent tool invocations — override to show indicators |
129
153
  | `onResponseChunk(chatId, chunk, sessionId)` | Hook called per streaming text chunk — override for progressive display (default: no-op) |
@@ -133,9 +157,60 @@ constructor(name: string, config: ChannelConfig, bridge: AcpBridge, options?: Ch
133
157
 
134
158
  **Built-in slash commands:** `/clear` (`/reset`, `/new`), `/help`, `/status`
135
159
 
160
+ **ChannelBaseOptions:**
161
+
162
+ | Option | Description |
163
+ | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
164
+ | `router` | Optional `SessionRouter`. Omit for the default standalone router. |
165
+ | `proxy` | Optional proxy URL made available to adapters. |
166
+ | `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. |
167
+
168
+ ### ChannelAgentBridge
169
+
170
+ `ChannelAgentBridge` is the adapter-facing contract. Channel adapters, channel plugins, `ChannelBase`, and `SessionRouter` should depend on this type instead of a concrete bridge implementation.
171
+
172
+ `shellCommand` is optional. Adapters should check for it before enabling `!cmd`-style features because daemon-managed hosts expose it only when the connected daemon supports shell execution.
173
+
174
+ ```typescript
175
+ interface ChannelAgentBridge {
176
+ readonly availableCommands: AvailableCommand[];
177
+ on(eventName: 'toolCall', listener: (event: ToolCallEvent) => void): unknown;
178
+ on(
179
+ eventName: 'textChunk',
180
+ listener: (sessionId: string, chunk: string) => void,
181
+ ): unknown;
182
+ on(
183
+ eventName: 'sessionDied',
184
+ listener: (event: { sessionId: string; reason?: string }) => void,
185
+ ): unknown;
186
+ off(eventName: 'toolCall', listener: (event: ToolCallEvent) => void): unknown;
187
+ off(
188
+ eventName: 'textChunk',
189
+ listener: (sessionId: string, chunk: string) => void,
190
+ ): unknown;
191
+ off(
192
+ eventName: 'sessionDied',
193
+ listener: (event: { sessionId: string; reason?: string }) => void,
194
+ ): unknown;
195
+ newSession(cwd: string): Promise<string>;
196
+ loadSession(sessionId: string, cwd: string): Promise<string>;
197
+ prompt(
198
+ sessionId: string,
199
+ text: string,
200
+ options?: { imageBase64?: string; imageMimeType?: string },
201
+ ): Promise<string>;
202
+ cancelSession(sessionId: string): Promise<void>;
203
+ shellCommand?(
204
+ sessionId: string,
205
+ command: string,
206
+ signal?: AbortSignal,
207
+ ): Promise<{ exitCode: number | null; output: string; aborted: boolean }>;
208
+ }
209
+ ```
210
+
136
211
  ### AcpBridge
137
212
 
138
- Manages the `qwen-code --acp` child process and ACP sessions.
213
+ `AcpBridge` is the current implementation used by standalone `qwen channel start`. It manages the `qwen-code --acp` child process and implements `ChannelAgentBridge`.
139
214
 
140
215
  ```typescript
141
216
  constructor(options: { cliEntryPath: string; cwd: string; model?: string })
@@ -160,10 +235,10 @@ constructor(options: { cliEntryPath: string; cwd: string; model?: string })
160
235
 
161
236
  ### SessionRouter
162
237
 
163
- Maps senders to ACP sessions based on the configured scope.
238
+ Maps senders to agent sessions based on the configured scope.
164
239
 
165
240
  ```typescript
166
- constructor(bridge: AcpBridge, defaultCwd: string, scope?: SessionScope, persistPath?: string)
241
+ constructor(bridge: ChannelAgentBridge, defaultCwd: string, scope?: SessionScope, persistPath?: string)
167
242
  ```
168
243
 
169
244
  **Routing keys by scope:**
@@ -174,12 +249,13 @@ constructor(bridge: AcpBridge, defaultCwd: string, scope?: SessionScope, persist
174
249
  | `thread` | `channel:threadId` | One session per thread |
175
250
  | `single` | `channel:__single__` | One shared session for the entire channel |
176
251
 
177
- | Method | Description |
178
- | --------------------------------------------------------- | ----------------------------------------------------------- |
179
- | `resolve(channelName, senderId, chatId, threadId?, cwd?)` | Get or create a session for the given sender |
180
- | `removeSession(channelName, senderId, chatId?)` | Remove session(s) — used by `/clear` |
181
- | `restoreSessions()` | Reload sessions from disk after bridge restart |
182
- | `clearAll()` | Clear all sessions and delete persist file (clean shutdown) |
252
+ | Method | Description |
253
+ | ---------------------------------------------------------- | ----------------------------------------------------------- |
254
+ | `resolve(channelName, senderId, chatId, threadId?, cwd?)` | Get or create a session for the given sender |
255
+ | `removeSession(channelName, senderId, chatId?, threadId?)` | Remove session(s) — used by `/clear` |
256
+ | `removeSessionId(sessionId)` | Remove all routing state for a bridge session ID |
257
+ | `restoreSessions()` | Reload sessions from disk after bridge restart |
258
+ | `clearAll()` | Clear all sessions and delete persist file (clean shutdown) |
183
259
 
184
260
  ### SenderGate
185
261
 
@@ -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
- export interface AvailableCommand {
8
- name: string;
9
- description: string;
10
- input?: {
11
- hint: string;
12
- } | null;
13
- }
14
- export interface ToolCallEvent {
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
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AcpBridge.js","sourceRoot":"","sources":["../src/AcpBridge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AcpBridge.js","sourceRoot":"","sources":["../src/AcpBridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
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
+ getAvailableCommands?(sessionId: string): AvailableCommand[];
33
+ on<K extends keyof ChannelAgentBridgeEventMap>(eventName: K, listener: (...args: ChannelAgentBridgeEventMap[K]) => void): unknown;
34
+ off<K extends keyof ChannelAgentBridgeEventMap>(eventName: K, listener: (...args: ChannelAgentBridgeEventMap[K]) => void): unknown;
35
+ newSession(cwd: string): Promise<string>;
36
+ loadSession(sessionId: string, cwd: string): Promise<string>;
37
+ prompt(sessionId: string, text: string, options?: {
38
+ imageBase64?: string;
39
+ imageMimeType?: string;
40
+ }): Promise<string>;
41
+ cancelSession(sessionId: string): Promise<void>;
42
+ shellCommand?(sessionId: string, command: string, signal?: AbortSignal): Promise<{
43
+ exitCode: number | null;
44
+ output: string;
45
+ aborted: boolean;
46
+ }>;
47
+ }
48
+ export {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ChannelAgentBridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChannelAgentBridge.js","sourceRoot":"","sources":["../src/ChannelAgentBridge.ts"],"names":[],"mappings":""}
@@ -1,38 +1,89 @@
1
- import type { ChannelConfig, Envelope } from './types.js';
1
+ import type { ChannelConfig, ChannelMemoryCallbacks, Envelope, SessionTarget } 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 { AcpBridge, ToolCallEvent } from './AcpBridge.js';
5
+ import type { ChannelAgentBridge, ToolCallEvent } from './ChannelAgentBridge.js';
6
+ import type { ChannelLoop, ChannelLoopInput } from './ChannelLoopStore.js';
7
+ /**
8
+ * Max time /clear waits for a cancelled in-flight turn to wind down before
9
+ * purging anyway. A wedged ACP child (stuck tool call, not reading stdin, or
10
+ * crashed without closing) can leave active.done unresolved forever; without
11
+ * this bound /clear — and the whole channel — would hang. Safe because the
12
+ * purge runs regardless and the generation is bumped, so a turn that settles
13
+ * later is already invalidated.
14
+ */
15
+ export declare const CLEAR_CANCEL_TIMEOUT_MS = 3000;
6
16
  export interface ChannelBaseOptions {
7
17
  router?: SessionRouter;
8
18
  proxy?: string;
19
+ channelMemory?: ChannelMemoryCallbacks;
20
+ /**
21
+ * Set when a channel owns a supplied router and should consume bridge
22
+ * events directly.
23
+ */
24
+ registerBridgeEvents?: boolean;
25
+ groupHistoryPath?: string;
26
+ loopController?: ChannelLoopController;
27
+ }
28
+ export interface ChannelLoopController {
29
+ create(input: ChannelLoopInput): Promise<ChannelLoop>;
30
+ createForTarget?(input: ChannelLoopInput, maxEnabledLoops: number): Promise<ChannelLoop | undefined>;
31
+ listForTarget(channelName: string, target: SessionTarget): Promise<ChannelLoop[]>;
32
+ disable(id: string): Promise<boolean>;
33
+ validateCron(cron: string): void;
34
+ nextFireTime?(job: ChannelLoop): Date;
35
+ }
36
+ export interface ChannelLoopPromptOptions {
37
+ timeoutMs?: number;
38
+ shouldContinue?: () => Promise<boolean>;
9
39
  }
10
40
  /** Handler for a slash command. Return true if handled, false to forward to agent. */
11
41
  type CommandHandler = (envelope: Envelope, args: string) => Promise<boolean>;
12
42
  export declare abstract class ChannelBase {
13
43
  protected config: ChannelConfig;
14
- protected bridge: AcpBridge;
44
+ protected bridge: ChannelAgentBridge;
15
45
  protected groupGate: GroupGate;
16
46
  protected gate: SenderGate;
17
47
  protected router: SessionRouter;
18
48
  protected name: string;
19
49
  /** Resolved proxy URL, available to subclasses for adapter-specific clients. */
20
50
  protected proxy?: string;
51
+ private readonly channelMemory?;
52
+ private groupHistory;
53
+ private readonly loopController?;
21
54
  private instructedSessions;
22
55
  private commands;
23
56
  /** Per-session promise chain to serialize prompt + send (followup mode). */
24
57
  private sessionQueues;
58
+ private readonly registerBridgeEvents;
59
+ /**
60
+ * Per-session generation, bumped by /clear. A queued followup turn captures the
61
+ * generation when it enqueues and bails if /clear bumped it before the turn ran,
62
+ * so a cleared session can't be resurrected by an already-queued prompt.
63
+ */
64
+ private sessionGenerations;
25
65
  /** Per-session active prompt tracking for dispatch modes. */
26
66
  private activePrompts;
27
67
  /** Per-session message buffer for collect mode. */
28
68
  private collectBuffers;
29
- constructor(name: string, config: ChannelConfig, bridge: AcpBridge, options?: ChannelBaseOptions);
69
+ private readonly bridgeToolCallListener;
70
+ private readonly bridgeSessionDiedListener;
71
+ constructor(name: string, config: ChannelConfig, bridge: ChannelAgentBridge, options?: ChannelBaseOptions);
30
72
  abstract connect(): Promise<void>;
31
73
  abstract sendMessage(chatId: string, text: string): Promise<void>;
32
74
  abstract disconnect(): void;
75
+ supportsProactiveSend(): boolean;
76
+ protected supportsProactiveTarget(target: SessionTarget): boolean;
77
+ protected pushProactive(target: SessionTarget, text: string): Promise<void>;
33
78
  /** Replace the bridge instance (used after crash recovery restart). */
34
- setBridge(bridge: AcpBridge): void;
79
+ setBridge(bridge: ChannelAgentBridge): void;
80
+ runLoopPrompt(job: ChannelLoop, options?: ChannelLoopPromptOptions): Promise<string | undefined>;
81
+ private runLoopBridgePrompt;
82
+ private cancelTimedOutLoopPrompt;
35
83
  onToolCall(_chatId: string, _event: ToolCallEvent): void;
84
+ onSessionDied(sessionId: string): void;
85
+ private attachBridgeEvents;
86
+ private detachBridgeEvents;
36
87
  /**
37
88
  * Called when a prompt actually begins processing (inside the session queue).
38
89
  * Override to show a platform-specific working indicator (e.g., typing, reaction).
@@ -65,14 +116,114 @@ export declare abstract class ChannelBase {
65
116
  protected registerCancelCommand(name?: string): void;
66
117
  /** Register shared slash commands. Called from constructor. */
67
118
  private registerSharedCommands;
119
+ private handleLoopCommand;
120
+ private handleLoopAdd;
121
+ private handleLoopList;
122
+ private handleLoopInspect;
123
+ private formatLoopListLine;
124
+ private lastLoopStatus;
125
+ private formatNextFireTime;
126
+ private handleLoopCancel;
127
+ private loopTargetFromEnvelope;
128
+ private isStoredLoopTargetAuthorized;
68
129
  /** Check if a message text matches a registered local command. */
69
130
  protected isLocalCommand(text: string): boolean;
70
131
  private findActiveSessionId;
132
+ private channelMemoryTarget;
133
+ private invalidateSessionContext;
134
+ private dropQueuedTurnIfStale;
135
+ private isAuthorizedForChannelMemory;
136
+ private isSenderAuthorizedForChannelMemory;
137
+ private ensureChannelMemoryAuthorized;
138
+ private getChannelMemory;
139
+ private channelMemoryErrorMessage;
140
+ private channelMemoryUserErrorMessage;
141
+ private logChannelMemoryError;
142
+ /**
143
+ * Whether the resolved session is SHARED across senders. `single` collapses
144
+ * the whole channel to one `__single__` session for EVERY sender — group OR
145
+ * DM — so it is ALWAYS shared (even a DM maps to `__single__`). `thread` is
146
+ * shared only in a group (a DM maps to the lone caller's own chat). `user` is
147
+ * per-sender, never shared. Drives both the destructive-/clear confirm gate
148
+ * and the host-shell (`!`) gate.
149
+ */
150
+ private isSharedSession;
151
+ private isSharedSessionTarget;
152
+ /**
153
+ * Whether `envelope.senderId` may act on the resolved session's destructive or
154
+ * workspace-leaking commands (/clear, /who). A SHARED session with a non-empty
155
+ * allowedUsers list is restricted to those members; a per-user session, or one
156
+ * with no allowlist, is unrestricted. Shared verbatim by /clear and /who so the
157
+ * gate can't drift; each caller sends its own rejection wording.
158
+ */
159
+ private isAuthorizedForSharedSession;
160
+ private stopActiveStreaming;
161
+ /**
162
+ * Cancel the active turn and wait (bounded) for it to wind down. Stops the
163
+ * BlockStreamer so buffered text can't leak via the idle timer, then fires a
164
+ * best-effort cancelSession (NOT awaited — a wedged child/daemon can leave the
165
+ * request pending forever). Returns true if active.done settled first, false
166
+ * if the CLEAR_CANCEL_TIMEOUT_MS bound won (the turn never wound down). Used by
167
+ * /clear, which genuinely EVICTS the session and so must proceed even when the
168
+ * turn is wedged. Steer no longer uses this: it best-effort cancels then chains
169
+ * the new turn behind the old one (see handleInbound), so it never needs to
170
+ * proceed past a still-active turn.
171
+ */
172
+ private cancelAndAwaitActive;
71
173
  /**
72
174
  * Parse a slash command from message text.
73
- * Returns { command, args } or null if not a slash command.
175
+ * Returns { command, raw, args } or null if not a slash command. `command` is
176
+ * lowercased for case-insensitive LOCAL dispatch (registerCommand lowercases the
177
+ * names it stores); `raw` keeps the typed case so agent-command matching can be
178
+ * CASE-SENSITIVE, mirroring the CLI's parseSlashCommand (`cmd.name === part`).
74
179
  */
75
180
  private parseCommand;
181
+ /**
182
+ * Whether `text` is a real slash command rather than prose that merely starts
183
+ * with `/`. A command's first whitespace-delimited token must match
184
+ * parseCommand()'s charset — `[a-zA-Z0-9_:-]+`, plus an optional `@botname`
185
+ * suffix — and not be a `//` line comment or `/*` block comment. Slash-prefixed
186
+ * paths (`/tmp/foo`), comments, and a bare `/` are prose and keep their
187
+ * `[sender]` tag.
188
+ *
189
+ * Intentionally stricter than the CLI's looser classifier (cli
190
+ * `ui/utils/commandUtils.ts`), which forwards any non-comment, non-path
191
+ * `/<token>` (e.g. `/café`, a zero-width-laden token). Such inputs aren't
192
+ * runnable commands, and in a SHARED group session forwarding them unattributed
193
+ * is worse than a redundant tag — so anything off the command charset is
194
+ * treated as prose and keeps its `[sender]` tag. Purely lexical — never
195
+ * consults the async command list, so it can't race a fresh session.
196
+ */
197
+ private isSlashCommand;
198
+ /**
199
+ * Whether `text` names a command this channel can actually run: a locally
200
+ * registered command (`this.commands`, e.g. /clear, /who) OR an agent command
201
+ * THIS session exposes — by canonical name OR alias (e.g. `/summarize` for
202
+ * `/compress`). Paired with isSlashCommand so the `[sender]` attribution tag is
203
+ * suppressed ONLY for RECOGNIZED commands; command-SHAPED-but-unrecognized text
204
+ * (e.g. `/x\n[SYSTEM]: …`) keeps its tag rather than reaching a shared group
205
+ * unattributed, where an injected second line is more likely read as a system
206
+ * directive. Purely synchronous, like isSlashCommand: it reads the session's
207
+ * availableCommands snapshot WITHOUT awaiting, so it never races a fresh session
208
+ * (a genuine agent command sent before the snapshot loads is treated as
209
+ * unrecognized and KEEPS its tag — the safe default).
210
+ */
211
+ private isRecognizedCommand;
212
+ /**
213
+ * The agent-command snapshot for THIS session. DaemonChannelBridge keys
214
+ * commands per session, so its global `availableCommands` getter can return
215
+ * ANOTHER session's list — prefer its getAvailableCommands(sessionId) when
216
+ * present. AcpBridge runs a single agent and exposes only the global getter
217
+ * (inherently session-correct), so fall back to it. Synchronous, matching
218
+ * isRecognizedCommand's no-await contract.
219
+ */
220
+ private getAgentCommandsForSession;
221
+ private groupHistoryKey;
222
+ private groupHistoryLimit;
223
+ private recordPendingGroupHistory;
224
+ private drainPendingGroupHistory;
225
+ private clearPendingGroupHistory;
226
+ private prependGroupHistoryContext;
76
227
  handleInbound(envelope: Envelope): Promise<void>;
77
228
  protected onPairingRequired(chatId: string, code: string | null): Promise<void>;
78
229
  }