@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/dist/index.d.ts CHANGED
@@ -1,12 +1,17 @@
1
1
  export { getGlobalQwenDir, resolvePath } from './paths.js';
2
2
  export { AcpBridge } from './AcpBridge.js';
3
- export type { AcpBridgeOptions, AvailableCommand, ToolCallEvent, } from './AcpBridge.js';
3
+ export type { AvailableCommand, ChannelAgentBridge, SessionDiedEvent, ToolCallEvent, } from './ChannelAgentBridge.js';
4
+ export type { AcpBridgeOptions } from './AcpBridge.js';
4
5
  export { DaemonChannelBridge } from './DaemonChannelBridge.js';
5
6
  export type { DaemonChannelBridgeOptions, DaemonChannelEvent, DaemonChannelSessionClient, DaemonChannelSessionFactory, DaemonChannelSessionFactoryRequest, DaemonPromptCompleteEvent, DaemonPermissionRequestEvent, DaemonPermissionResolvedEvent, } from './DaemonChannelBridge.js';
6
7
  export { BlockStreamer } from './BlockStreamer.js';
7
8
  export type { BlockStreamerOptions } from './BlockStreamer.js';
8
9
  export { ChannelBase } from './ChannelBase.js';
9
- export type { ChannelBaseOptions } from './ChannelBase.js';
10
+ export type { ChannelBaseOptions, ChannelLoopController, } from './ChannelBase.js';
11
+ export { ChannelLoopScheduler } from './ChannelLoopScheduler.js';
12
+ export type { ChannelLoopSchedulerOptions, ChannelLoopRunner, } from './ChannelLoopScheduler.js';
13
+ export { ChannelLoopStore } from './ChannelLoopStore.js';
14
+ export type { ChannelLoop, ChannelLoopInput, ChannelLoopPatch, ChannelLoopStatus, ChannelLoopStoreOptions, } from './ChannelLoopStore.js';
10
15
  export { PairingStore } from './PairingStore.js';
11
16
  export type { PairingRequest } from './PairingStore.js';
12
17
  export { GroupGate } from './GroupGate.js';
@@ -14,4 +19,5 @@ export type { GroupCheckResult } from './GroupGate.js';
14
19
  export { SenderGate } from './SenderGate.js';
15
20
  export type { SenderCheckResult } from './SenderGate.js';
16
21
  export { SessionRouter } from './SessionRouter.js';
22
+ export { sanitizeSenderName, sanitizePromptText, sanitizeLogText, } from './sanitize.js';
17
23
  export type { Attachment, BlockStreamingChunkConfig, BlockStreamingCoalesceConfig, ChannelConfig, ChannelPlugin, ChannelType, DispatchMode, Envelope, GroupConfig, GroupPolicy, SenderPolicy, SessionScope, SessionTarget, } from './types.js';
package/dist/index.js CHANGED
@@ -3,8 +3,11 @@ export { AcpBridge } from './AcpBridge.js';
3
3
  export { DaemonChannelBridge } from './DaemonChannelBridge.js';
4
4
  export { BlockStreamer } from './BlockStreamer.js';
5
5
  export { ChannelBase } from './ChannelBase.js';
6
+ export { ChannelLoopScheduler } from './ChannelLoopScheduler.js';
7
+ export { ChannelLoopStore } from './ChannelLoopStore.js';
6
8
  export { PairingStore } from './PairingStore.js';
7
9
  export { GroupGate } from './GroupGate.js';
8
10
  export { SenderGate } from './SenderGate.js';
9
11
  export { SessionRouter } from './SessionRouter.js';
12
+ export { sanitizeSenderName, sanitizePromptText, sanitizeLogText, } from './sanitize.js';
10
13
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAM3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAW/D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAQ3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAW/D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAK/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAKjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAQzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,GAChB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Characters that must be neutralized in ANY attacker-controlled text we embed
3
+ * into a prompt, independent of the wrapper's own delimiters: the C1 control
4
+ * block (U+0080-U+009F) - notably NEL (U+0085), a Unicode line break (UAX#14 BK
5
+ * class) that renders as a new line, i.e. prompt-line injection - plus the
6
+ * Unicode line/paragraph separators (U+2028/U+2029, likewise rendered as
7
+ * newlines) and the bidirectional override/isolate controls (U+202A-U+202E,
8
+ * U+2066-U+2069 -> trojan-source, where the visual order differs from the
9
+ * logical byte order). Also strips common zero-width format chars that make
10
+ * visually identical names/text compare differently. ASCII C0/DEL (incl. CR/LF)
11
+ * are stripped by each caller.
12
+ */
13
+ export declare const PROMPT_UNSAFE_INVISIBLES: RegExp;
14
+ /**
15
+ * Neutralize a platform display name before embedding it in a `[name]` prompt
16
+ * tag: strip the bracket/newline delimiters, C0/DEL control chars, and the
17
+ * Unicode line/bidi controls above that would let a crafted nickname break out
18
+ * of the tag, inject extra lines, or smuggle terminal escape sequences, then
19
+ * cap the length. Shared by ChannelBase group attribution and adapters that
20
+ * self-prefix (e.g. QQ), so the rules stay identical everywhere.
21
+ */
22
+ export declare function sanitizeSenderName(name: string): string;
23
+ /**
24
+ * Neutralize attacker-controlled text embedded inside a `"..."` prompt wrapper
25
+ * (reply quotes, attachment filenames): strip C0/DEL control chars, the
26
+ * wrapper's own quote/bracket delimiters, and the Unicode line/bidi controls
27
+ * above, then cap the length. Shared so the reply-quote and filename paths
28
+ * can't drift apart. On truncation a single-char ellipsis is appended (kept
29
+ * within maxLen) so the agent can tell a quote/filename was cut rather than
30
+ * silently ending mid-token.
31
+ */
32
+ export declare function sanitizeQuotedText(text: string, maxLen: number): string;
33
+ export declare function sanitizePromptText(text: string): string;
34
+ /**
35
+ * Neutralize an attacker-influenced filesystem path before rendering it on
36
+ * its own line in a prompt (`... saved to: <path>`). Unlike
37
+ * sanitizeQuotedText, this PRESERVES `[`, `]`, `"`, and spaces: those are
38
+ * valid, common path characters (e.g. Next.js `app/[slug]/page.tsx`, a
39
+ * quoted segment, a space in a folder name), and a path rendered alone on a
40
+ * line cannot use them to break out of that line, so stripping them would
41
+ * only corrupt the path and make the agent's read-file tool miss a file that
42
+ * exists on disk. We strip ONLY what can break or reorder the line: C0/DEL
43
+ * controls (incl. CR/LF -> prompt-line injection) and the Unicode line/para
44
+ * separators + bidi overrides (trojan-source). Length is capped generously
45
+ * (1024) as defense-in-depth: well beyond any real path, but enough to stop a
46
+ * pathological attacker filename from ballooning the prompt unboundedly.
47
+ */
48
+ export declare function sanitizePromptPath(path: string): string;
49
+ /**
50
+ * Neutralize attacker-controlled text before it is written to a single-line
51
+ * stderr audit/diagnostic log. Caps to `maxLen` code points, renders ASCII
52
+ * newlines as a visible `\n` escape (so a multi-line payload stays one readable
53
+ * log line instead of collapsing to a space), then strips everything that could
54
+ * forge or corrupt a log line: PROMPT_UNSAFE_INVISIBLES (the C1 block incl. NEL
55
+ * U+0085, the Unicode line/paragraph separators U+2028/U+2029, and the bidi
56
+ * override/isolate controls — all of which render as a line break or reorder
57
+ * text) AND the C0/DEL controls (CR could overwrite the line, ESC could inject
58
+ * ANSI/OSC). Shared by every audit-log site so the strip set can't drift apart.
59
+ */
60
+ export declare function sanitizeLogText(text: string, maxLen: number): string;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Characters that must be neutralized in ANY attacker-controlled text we embed
3
+ * into a prompt, independent of the wrapper's own delimiters: the C1 control
4
+ * block (U+0080-U+009F) - notably NEL (U+0085), a Unicode line break (UAX#14 BK
5
+ * class) that renders as a new line, i.e. prompt-line injection - plus the
6
+ * Unicode line/paragraph separators (U+2028/U+2029, likewise rendered as
7
+ * newlines) and the bidirectional override/isolate controls (U+202A-U+202E,
8
+ * U+2066-U+2069 -> trojan-source, where the visual order differs from the
9
+ * logical byte order). Also strips common zero-width format chars that make
10
+ * visually identical names/text compare differently. ASCII C0/DEL (incl. CR/LF)
11
+ * are stripped by each caller.
12
+ */
13
+ export const PROMPT_UNSAFE_INVISIBLES = /[\u0080-\u009f\u200b-\u200d\u2028\u2029\u202a-\u202e\u2060\u2066-\u2069\ufeff]/g;
14
+ /**
15
+ * Truncate to at most `max` Unicode CODE POINTS (not UTF-16 code units). A cap
16
+ * applied with `.slice` counts code units, so one landing mid-surrogate-pair
17
+ * (e.g. an emoji \ud83c\udf89 = 2 units) leaves a lone surrogate that renders as `\ufffd`.
18
+ * `Array.from` iterates by code point, so slicing it never splits a pair.
19
+ */
20
+ function truncateCodePoints(str, max) {
21
+ const cp = Array.from(str);
22
+ return cp.length > max ? cp.slice(0, max).join('') : str;
23
+ }
24
+ /**
25
+ * Neutralize a platform display name before embedding it in a `[name]` prompt
26
+ * tag: strip the bracket/newline delimiters, C0/DEL control chars, and the
27
+ * Unicode line/bidi controls above that would let a crafted nickname break out
28
+ * of the tag, inject extra lines, or smuggle terminal escape sequences, then
29
+ * cap the length. Shared by ChannelBase group attribution and adapters that
30
+ * self-prefix (e.g. QQ), so the rules stay identical everywhere.
31
+ */
32
+ export function sanitizeSenderName(name) {
33
+ // A name made entirely of strippable chars collapses to all-spaces; trim()-ing
34
+ // it to '' lets the `|| 'unknown'` fallback fire so the [name] tag is never an
35
+ // anonymous `[]`. Both callers embed the result with no fallback of their own.
36
+ const cleaned = name
37
+ .replace(PROMPT_UNSAFE_INVISIBLES, ' ')
38
+ // eslint-disable-next-line no-control-regex
39
+ .replace(/[\u0000-\u001f\u007f]/g, ' ')
40
+ .replace(/[[\]\r\n]/g, ' ');
41
+ // Truncate on code-point boundaries so an emoji nick capped mid-pair can't
42
+ // leave a lone surrogate (renders as `�`).
43
+ return truncateCodePoints(cleaned, 64).trim() || 'unknown';
44
+ }
45
+ /**
46
+ * Neutralize attacker-controlled text embedded inside a `"..."` prompt wrapper
47
+ * (reply quotes, attachment filenames): strip C0/DEL control chars, the
48
+ * wrapper's own quote/bracket delimiters, and the Unicode line/bidi controls
49
+ * above, then cap the length. Shared so the reply-quote and filename paths
50
+ * can't drift apart. On truncation a single-char ellipsis is appended (kept
51
+ * within maxLen) so the agent can tell a quote/filename was cut rather than
52
+ * silently ending mid-token.
53
+ */
54
+ export function sanitizeQuotedText(text, maxLen) {
55
+ const cleaned = text
56
+ .replace(PROMPT_UNSAFE_INVISIBLES, ' ')
57
+ // eslint-disable-next-line no-control-regex
58
+ .replace(/[\u0000-\u001f\u007f]/g, ' ')
59
+ .replace(/["[\]]/g, ' ');
60
+ // Count/slice by CODE POINT, not UTF-16 unit, so a cap landing mid-surrogate-
61
+ // pair can't leave a lone surrogate (`�`). On truncation keep maxLen-1 code
62
+ // points + the single-char ellipsis, so the result stays within maxLen.
63
+ const cp = Array.from(cleaned);
64
+ return cp.length > maxLen ? cp.slice(0, maxLen - 1).join('') + '…' : cleaned;
65
+ }
66
+ export function sanitizePromptText(text) {
67
+ return (text
68
+ .replace(PROMPT_UNSAFE_INVISIBLES, ' ')
69
+ .replace(/^([ \t]*)\[([^\]\r\n]{1,64})\](:?)/gm, '$1$2$3')
70
+ // Fold ASCII C0/DEL, including CR/LF/TAB, so attacker-controlled group
71
+ // text cannot create prompt lines outside the adapter's sender attribution.
72
+ // eslint-disable-next-line no-control-regex
73
+ .replace(/[\u0000-\u001f\u007f]/g, ' '));
74
+ }
75
+ /**
76
+ * Neutralize an attacker-influenced filesystem path before rendering it on
77
+ * its own line in a prompt (`... saved to: <path>`). Unlike
78
+ * sanitizeQuotedText, this PRESERVES `[`, `]`, `"`, and spaces: those are
79
+ * valid, common path characters (e.g. Next.js `app/[slug]/page.tsx`, a
80
+ * quoted segment, a space in a folder name), and a path rendered alone on a
81
+ * line cannot use them to break out of that line, so stripping them would
82
+ * only corrupt the path and make the agent's read-file tool miss a file that
83
+ * exists on disk. We strip ONLY what can break or reorder the line: C0/DEL
84
+ * controls (incl. CR/LF -> prompt-line injection) and the Unicode line/para
85
+ * separators + bidi overrides (trojan-source). Length is capped generously
86
+ * (1024) as defense-in-depth: well beyond any real path, but enough to stop a
87
+ * pathological attacker filename from ballooning the prompt unboundedly.
88
+ */
89
+ export function sanitizePromptPath(path) {
90
+ const cleaned = path
91
+ .replace(PROMPT_UNSAFE_INVISIBLES, ' ')
92
+ // eslint-disable-next-line no-control-regex
93
+ .replace(/[\u0000-\u001f\u007f]/g, ' ');
94
+ // Cap by code point so a path ending in an emoji can't be split mid-pair.
95
+ return truncateCodePoints(cleaned, 1024);
96
+ }
97
+ /**
98
+ * Neutralize attacker-controlled text before it is written to a single-line
99
+ * stderr audit/diagnostic log. Caps to `maxLen` code points, renders ASCII
100
+ * newlines as a visible `\n` escape (so a multi-line payload stays one readable
101
+ * log line instead of collapsing to a space), then strips everything that could
102
+ * forge or corrupt a log line: PROMPT_UNSAFE_INVISIBLES (the C1 block incl. NEL
103
+ * U+0085, the Unicode line/paragraph separators U+2028/U+2029, and the bidi
104
+ * override/isolate controls — all of which render as a line break or reorder
105
+ * text) AND the C0/DEL controls (CR could overwrite the line, ESC could inject
106
+ * ANSI/OSC). Shared by every audit-log site so the strip set can't drift apart.
107
+ */
108
+ export function sanitizeLogText(text, maxLen) {
109
+ return (truncateCodePoints(text, maxLen)
110
+ // Render real newlines visibly BEFORE the control strip, so the common
111
+ // ASCII-newline case shows as `\n` rather than collapsing to a space.
112
+ .replace(/\n/g, '\\n')
113
+ .replace(PROMPT_UNSAFE_INVISIBLES, ' ')
114
+ // eslint-disable-next-line no-control-regex
115
+ .replace(/[\u0000-\u001f\u007f]/g, ' '));
116
+ }
117
+ //# sourceMappingURL=sanitize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,wBAAwB,GACnC,iFAAiF,CAAC;AAEpF;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,GAAW,EAAE,GAAW;IAClD,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3B,OAAO,EAAE,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAC3D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,+EAA+E;IAC/E,+EAA+E;IAC/E,+EAA+E;IAC/E,MAAM,OAAO,GAAG,IAAI;SACjB,OAAO,CAAC,wBAAwB,EAAE,GAAG,CAAC;QACvC,4CAA4C;SAC3C,OAAO,CAAC,wBAAwB,EAAE,GAAG,CAAC;SACtC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;IAC9B,2EAA2E;IAC3E,2CAA2C;IAC3C,OAAO,kBAAkB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,SAAS,CAAC;AAC7D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,MAAc;IAC7D,MAAM,OAAO,GAAG,IAAI;SACjB,OAAO,CAAC,wBAAwB,EAAE,GAAG,CAAC;QACvC,4CAA4C;SAC3C,OAAO,CAAC,wBAAwB,EAAE,GAAG,CAAC;SACtC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAC3B,8EAA8E;IAC9E,4EAA4E;IAC5E,wEAAwE;IACxE,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,CACL,IAAI;SACD,OAAO,CAAC,wBAAwB,EAAE,GAAG,CAAC;SACtC,OAAO,CAAC,sCAAsC,EAAE,QAAQ,CAAC;QAC1D,uEAAuE;QACvE,4EAA4E;QAC5E,4CAA4C;SAC3C,OAAO,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAC1C,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,OAAO,GAAG,IAAI;SACjB,OAAO,CAAC,wBAAwB,EAAE,GAAG,CAAC;QACvC,4CAA4C;SAC3C,OAAO,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;IAC1C,0EAA0E;IAC1E,OAAO,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,MAAc;IAC1D,OAAO,CACL,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC;QAC9B,uEAAuE;QACvE,sEAAsE;SACrE,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,wBAAwB,EAAE,GAAG,CAAC;QACvC,4CAA4C;SAC3C,OAAO,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAC1C,CAAC;AACJ,CAAC"}
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AcpBridge } from './AcpBridge.js';
1
+ import type { ChannelAgentBridge } from './ChannelAgentBridge.js';
2
2
  import type { ChannelBase, ChannelBaseOptions } from './ChannelBase.js';
3
3
  export type SenderPolicy = 'allowlist' | 'pairing' | 'open';
4
4
  export type SessionScope = 'user' | 'thread' | 'single';
@@ -8,6 +8,7 @@ export type DispatchMode = 'collect' | 'steer' | 'followup';
8
8
  export interface GroupConfig {
9
9
  requireMention?: boolean;
10
10
  dispatchMode?: DispatchMode;
11
+ groupHistoryLimit?: number;
11
12
  }
12
13
  export interface BlockStreamingChunkConfig {
13
14
  /** Minimum characters before emitting a block. Default: 400. */
@@ -32,8 +33,9 @@ export interface ChannelConfig {
32
33
  instructions?: string;
33
34
  model?: string;
34
35
  groupPolicy: GroupPolicy;
36
+ groupHistoryLimit?: number;
35
37
  groups: Record<string, GroupConfig>;
36
- /** Dispatch mode for concurrent messages. Default: 'collect'. */
38
+ /** Dispatch mode for concurrent messages. Default: 'steer' (resolved in ChannelBase.handleInbound). */
37
39
  dispatchMode?: DispatchMode;
38
40
  /** Enable block streaming — emit completed blocks as separate messages. */
39
41
  blockStreaming?: 'on' | 'off';
@@ -74,12 +76,37 @@ export interface Envelope {
74
76
  imageMimeType?: string;
75
77
  /** Structured attachments (images, files, audio, video). */
76
78
  attachments?: Attachment[];
79
+ /**
80
+ * Marks an envelope whose `text` ALREADY carries its `[sender]` attribution, so
81
+ * handleInbound must NOT re-prefix it. Set in two places: on a synthetic
82
+ * collect-mode re-entry (coalesced text already carries each message's prefix), AND
83
+ * by the QQ adapter on a REAL inbound it self-prefixes as `[name]: …`. QQ
84
+ * neutralizes that embedded name with sanitizeSenderName at the source (QQChannel),
85
+ * so the self-prefixed name reaching the prompt is already sanitized — setting this
86
+ * flag does not bypass sanitization.
87
+ */
88
+ alreadyPrefixed?: true;
77
89
  }
78
90
  export interface SessionTarget {
79
91
  channelName: string;
80
92
  senderId: string;
81
93
  chatId: string;
82
94
  threadId?: string;
95
+ isGroup?: boolean;
96
+ }
97
+ export interface ChannelMemoryTarget {
98
+ channelName: string;
99
+ chatId: string;
100
+ threadId?: string;
101
+ }
102
+ export interface ChannelMemoryWriteResult {
103
+ changed: boolean;
104
+ filePath?: string;
105
+ }
106
+ export interface ChannelMemoryCallbacks {
107
+ readChannelMemory(target: ChannelMemoryTarget): Promise<string>;
108
+ appendChannelMemory(target: ChannelMemoryTarget, text: string): Promise<ChannelMemoryWriteResult>;
109
+ clearChannelMemory(target: ChannelMemoryTarget): Promise<ChannelMemoryWriteResult>;
83
110
  }
84
111
  /**
85
112
  * A channel plugin registers a channel type and provides a factory
@@ -97,5 +124,5 @@ export interface ChannelPlugin {
97
124
  */
98
125
  requiredConfigFields?: string[];
99
126
  /** Create a channel adapter instance. */
100
- createChannel(name: string, config: ChannelConfig & Record<string, unknown>, bridge: AcpBridge, options?: ChannelBaseOptions): ChannelBase;
127
+ createChannel(name: string, config: ChannelConfig & Record<string, unknown>, bridge: ChannelAgentBridge, options?: ChannelBaseOptions): ChannelBase;
101
128
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qwen-code/channel-base",
3
- "version": "0.19.3",
3
+ "version": "0.19.4",
4
4
  "description": "Base channel infrastructure for Qwen Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",