Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
134 commits
Select commit Hold shift + click to select a range
ca89997
feat(channel): add QQ Bot channel adapter
Eric-GoodBoy-Tech Jun 16, 2026
6d0a2cf
feat(qqbot): add QR login, group chat support with typed events
Eric-GoodBoy-Tech Jun 16, 2026
364bba7
fix(qqbot): address PR review — lint errors, token refresh, security
Eric-GoodBoy-Tech Jun 16, 2026
82e8d5b
fix(qqbot): add qqbot to build order, fix ESLint default-case
Eric-GoodBoy-Tech Jun 16, 2026
b01070b
feat(qqbot): prepend sender name in group messages for shared context
Eric-GoodBoy-Tech Jun 17, 2026
b3991a0
feat(qqbot): cross-server context continuation via SessionRouter pers…
Eric-GoodBoy-Tech Jun 17, 2026
340c3ef
feat(qqbot): add Session Resume + reconnect retry resilience
Eric-GoodBoy-Tech Jun 17, 2026
1d593e2
fix(qqbot): address review feedback from wenshao
Eric-GoodBoy-Tech Jun 17, 2026
d485fd3
fix(qqbot): address 3rd review from doudouOUC (12 issues)
Eric-GoodBoy-Tech Jun 17, 2026
0cee74a
fix(qqbot): robustness round — RESUMED, token expiry, SSRF, disposed,…
Eric-GoodBoy-Tech Jun 18, 2026
9f4b198
refactor(qqbot): split into modules — api, accounts, login
Eric-GoodBoy-Tech Jun 18, 2026
86c8618
feat(qqbot): markdown message support (msg_type: 2)
Eric-GoodBoy-Tech Jun 18, 2026
28ba879
fix(qqbot): defensive patches from complete review
Eric-GoodBoy-Tech Jun 18, 2026
07374d5
fix(qqbot): guard against empty content in C2C and group handlers
Eric-GoodBoy-Tech Jun 18, 2026
fa3fd8b
fix(qqbot): close remaining review gaps — disposed guard, connectReje…
Eric-GoodBoy-Tech Jun 18, 2026
526e6e0
fix(qqbot): address wenshao review — RESUME restore removal, disposed…
Eric-GoodBoy-Tech Jun 18, 2026
37b1826
fix(qqbot): markdown fallback to plain text on rejection
Eric-GoodBoy-Tech Jun 18, 2026
7eaf2df
docs(qqbot): clarify markdown permission — Open Platform has no gate,…
Eric-GoodBoy-Tech Jun 18, 2026
3be44c2
feat(qqbot): add Ark (msg_type=3) and Media (msg_type=7) message support
Eric-GoodBoy-Tech Jun 18, 2026
b2b9da7
feat(qqbot): auto-route !ark / !media commands from LLM text via send…
Eric-GoodBoy-Tech Jun 18, 2026
06426db
feat(qqbot): inject channel instructions for ark/media commands
Eric-GoodBoy-Tech Jun 18, 2026
765e8c6
feat(qqbot): gate ark/media behind config flags (enableArk/enableMedia)
Eric-GoodBoy-Tech Jun 18, 2026
db9e1ad
refactor(qqbot): extract resolveRoute() to eliminate duplication acro…
Eric-GoodBoy-Tech Jun 18, 2026
cba8b61
chore(qqbot): remove Ark and Media message support
Eric-GoodBoy-Tech Jun 18, 2026
b34c50f
fix(qqbot): robustness patches for review findings
Eric-GoodBoy-Tech Jun 18, 2026
8df4df1
docs(channel): add QQ Bot user documentation
Eric-GoodBoy-Tech Jun 18, 2026
ecf048e
docs(qqbot): fix prerequisites — QR login needs no developer account
Eric-GoodBoy-Tech Jun 18, 2026
8ad5c3c
docs(qqbot): emphasize QR login, keep developer portal as secondary path
Eric-GoodBoy-Tech Jun 18, 2026
21295c0
docs(qqbot): remove Images and Files section — not supported in chann…
Eric-GoodBoy-Tech Jun 18, 2026
08f7e81
test(qqbot): add unit tests for send utilities
Eric-GoodBoy-Tech Jun 18, 2026
dc5bc32
test(qqbot): add sendMessage flow tests with mocked API
Eric-GoodBoy-Tech Jun 18, 2026
e4c7d16
test(qqbot): fix review issues — add missing edge cases
Eric-GoodBoy-Tech Jun 18, 2026
fb5fcf6
chore(qqbot): suppress CodeQL ReDoS false positives
Eric-GoodBoy-Tech Jun 18, 2026
462e31d
fix(qqbot): allow QR-code-only login and guard qrConnect return
Eric-GoodBoy-Tech Jun 18, 2026
08b1090
chore(qqbot): add comments for requiredConfigFields and qrConnect guard
Eric-GoodBoy-Tech Jun 18, 2026
7bdce62
fix(qqbot): replace quadratic regexes with linear patterns, remove fa…
Eric-GoodBoy-Tech Jun 18, 2026
45d650a
fix(qqbot): split hasMarkdownSyntax into individual tests to pass CodeQL
Eric-GoodBoy-Tech Jun 18, 2026
5ab5f26
fix(qqbot): replace markdown link regex with indexOf to eliminate Cod…
Eric-GoodBoy-Tech Jun 18, 2026
7473d61
fix(qqbot): streaming improvements - idle flush, remove splitText, re…
Eric-GoodBoy-Tech Jun 26, 2026
9faa163
fix(qqbot): address PR #5902 review — concurrency, resilience, diagno…
Eric-GoodBoy-Tech Jun 26, 2026
fd80863
fix(qqbot): group full-message handling, slash command unification, n…
Eric-GoodBoy-Tech Jun 27, 2026
5fbf374
fix(qqbot): use @mention format (<@OPENID>) instead of (openid: OPENI…
Eric-GoodBoy-Tech Jun 28, 2026
0f93145
fix(qqbot): remove botOpenId from instructions (user_openid incompati…
Eric-GoodBoy-Tech Jun 28, 2026
0c3a7ef
fix(qqbot): remove openid injection, use mentions.is_you only for @bo…
Eric-GoodBoy-Tech Jun 28, 2026
13fc806
fix(qqbot): add atMention context marker and wake/silence judgment rules
Eric-GoodBoy-Tech Jun 28, 2026
fd0a0ae
fix(qqbot): address PR #5902 review rounds 2-3
Eric-GoodBoy-Tech Jun 28, 2026
3bf36f7
fix(qqbot): address PR #5902 review round 4
Eric-GoodBoy-Tech Jun 28, 2026
427c3a4
test(qqbot): add 90 tests for streaming, persistence, events, and sen…
Eric-GoodBoy-Tech Jun 28, 2026
760592c
fix(qqbot): address review round 5 — 28 issues (timers, handlers, per…
Eric-GoodBoy-Tech Jun 28, 2026
7b4d0a8
fix(qqbot): revert handleGroupAll — raw content with OPENID tags inte…
Eric-GoodBoy-Tech Jun 28, 2026
682b788
fix(qqbot): review round 6 — msgSeqMap fallback, author guard, readyT…
Eric-GoodBoy-Tech Jun 29, 2026
e2f0620
fix(qqbot): clear readyTimeout before reassign on INVALID_SESSION
Eric-GoodBoy-Tech Jun 29, 2026
14240d9
fix(qqbot): review R9 — persist msgSeqMap, blockStreaming guard, onRe…
Eric-GoodBoy-Tech Jun 29, 2026
50371a3
fix(qqbot): use ?? instead of || for buffer fallback in onResponseCom…
Eric-GoodBoy-Tech Jun 29, 2026
4bf50e0
fix(qqbot): saveQQState on msgSeqMap error-path rollback
Eric-GoodBoy-Tech Jun 29, 2026
ab66109
fix(qqbot): address 6 unresolved review threads
Eric-GoodBoy-Tech Jun 29, 2026
d0975d5
chore: sync package-lock.json after rebase onto main
Eric-GoodBoy-Tech Jun 29, 2026
5b5879d
fix(qqbot): 3 review issues — safeName, slash log, mentions check
Eric-GoodBoy-Tech Jun 29, 2026
4aed8b0
fix(qqbot): 3 review issues — sanitizePromptText, slash log token, @a…
Eric-GoodBoy-Tech Jun 29, 2026
72abc15
fix(qqbot): .unref(), listener leak, sanitizer tests, allowMention co…
Eric-GoodBoy-Tech Jun 30, 2026
e23f6ba
fix(qqbot): sanitizeLogText missing maxLen arg, handleGroup allowMent…
Eric-GoodBoy-Tech Jun 30, 2026
99450fc
fix(qqbot): AcpBridge → ChannelAgentBridge type rename after rebase
Eric-GoodBoy-Tech Jun 30, 2026
7b52a7c
fix(qqbot): sessionDied listener, chatTypeMap persistence for non-@ g…
Eric-GoodBoy-Tech Jun 30, 2026
4a44063
fix(qqbot): replace hard bot block with [bot] marker + prompt judgment
Eric-GoodBoy-Tech Jun 30, 2026
1db1c21
fix(qqbot): R15 — TS2322, log sanitize, toolCall dup, flushQQState, c…
Eric-GoodBoy-Tech Jun 30, 2026
e47ed05
feat(qqbot): add chatTypes config for cron/unsolicited message routing
Eric-GoodBoy-Tech Jun 30, 2026
aa91ff4
fix(qqbot): passive→active downgrade on 429 + base tsconfig verbatim fix
Eric-GoodBoy-Tech Jun 30, 2026
3fcdef6
fix(qqbot): reduce log noise, add onToolCall diagnostic
Eric-GoodBoy-Tech Jun 30, 2026
d563eda
fix(qqbot): show sender OPENID in text, clarify @mention prompt
Eric-GoodBoy-Tech Jun 30, 2026
ff394f7
fix(qqbot): extract botOpenId from READY event, use real OPENID in pr…
Eric-GoodBoy-Tech Jun 30, 2026
ac8fe12
fix(qqbot): fetch bot OPENID via @me API endpoint instead of READY us…
Eric-GoodBoy-Tech Jun 30, 2026
16cc572
fix(qqbot): add @me diagnostic log in all 3 READY paths
Eric-GoodBoy-Tech Jun 30, 2026
3284480
fix(qqbot): extract bot OPENID from mentions instead of @me endpoint
Eric-GoodBoy-Tech Jun 30, 2026
0e4e768
chore(qqbot): remove diagnostic logs
Eric-GoodBoy-Tech Jun 30, 2026
83aa22c
fix(qqbot): permanent textChunk listener for cron/non-prompt messages
Eric-GoodBoy-Tech Jun 30, 2026
fe9d35e
fix(qqbot): accumulate cron textChunk with idle timer, avoid fragmenting
Eric-GoodBoy-Tech Jun 30, 2026
93bc952
fix(qqbot): guard cron textChunk with _ready flag to prevent stale ou…
Eric-GoodBoy-Tech Jun 30, 2026
bb00bea
fix(qqbot): reorder passive retry chain, add active fallback for 400/429
Eric-GoodBoy-Tech Jun 30, 2026
45c447d
fix(qqbot): remove unnecessary plain-text passive retry
Eric-GoodBoy-Tech Jun 30, 2026
eefa7d9
fix(qqbot): bump version to 0.19.3, clean dead code comments
Eric-GoodBoy-Tech Jun 30, 2026
1b05c90
fix(qqbot): fix PR CI failure
Eric-GoodBoy-Tech Jun 30, 2026
9157b1e
fix(qqbot): address wenshao R15 review - bot guard, ready timing, TS4…
Eric-GoodBoy-Tech Jun 30, 2026
5e9182a
fix(qqbot): remove isBot early return, use [bot] prefix per design
Eric-GoodBoy-Tech Jun 30, 2026
be9ea46
fix(qqbot): address deep review - connectGateway hang, dedup botOpenI…
Eric-GoodBoy-Tech Jun 30, 2026
bebab86
fix(qqbot): idle-flush buffer restore on failure, token exhaustion re…
Eric-GoodBoy-Tech Jun 30, 2026
8de3edc
fix(qqbot): coldStart reset, isReconnecting guard, handleGroup trim
Eric-GoodBoy-Tech Jun 30, 2026
cc8f5dd
fix(qqbot): setBridge re-attach cron handler, connect() Promise catch
Eric-GoodBoy-Tech Jul 1, 2026
3a90574
fix(qqbot): orphaned timer tracking, cron handler re-attach, dedup or…
Eric-GoodBoy-Tech Jul 1, 2026
68c0fe1
fix(qqbot): plain-text fallback for non-passive markdown rejections
Eric-GoodBoy-Tech Jul 1, 2026
ecefbb9
fix(qqbot): idle-flush race, slash sanitize, RESUMED _ready, cronBuff…
Eric-GoodBoy-Tech Jul 1, 2026
f0f6de9
fix(qqbot): cron buffer restore on failure, conditional GROUP_MESSAGE…
Eric-GoodBoy-Tech Jul 1, 2026
6733af4
fix(qqbot): plain-text seq, cronBuffer cleanup on group remove
Eric-GoodBoy-Tech Jul 1, 2026
f6c105f
fix(qqbot): disposed reconnect lock, add experimental config
Eric-GoodBoy-Tech Jul 1, 2026
115557d
feat(qqbot): gate cron features behind experimental flag
Eric-GoodBoy-Tech Jul 1, 2026
b396441
refactor(qqbot): rename experimental to cron-msg-experimental
Eric-GoodBoy-Tech Jul 1, 2026
3c36be6
fix(qqbot): resolve 7 critical review threads from wenshao
Eric-GoodBoy-Tech Jul 1, 2026
a785ff1
fix(qqbot): evict chatTypeMap/groupActiveMsgEnabled in replyMsgId cle…
Eric-GoodBoy-Tech Jul 1, 2026
a6e8e5a
fix(qqbot): add diagnostic log when active retry hits HTTP 429
Eric-GoodBoy-Tech Jul 1, 2026
72c6ad7
fix: restore @qwen-code/channel-qqbot version to 0.19.3 in package-lo…
Eric-GoodBoy-Tech Jul 1, 2026
87c0d4f
fix: reject ws: protocol in validateGatewayUrl to prevent cleartext t…
Eric-GoodBoy-Tech Jul 1, 2026
a4bbe38
fix: multiple QQ channel review thread fixes
Eric-GoodBoy-Tech Jul 1, 2026
a008fa2
fix(qqbot): align validateGatewayUrl JSDoc with wss:-only validation
Eric-GoodBoy-Tech Jul 1, 2026
8a8bdda
fix(qqbot): clear onToolCall buffer before send, not in .then()
Eric-GoodBoy-Tech Jul 1, 2026
04fe383
fix: add missing audit:runtime:critical script to package.json
Eric-GoodBoy-Tech Jul 1, 2026
fd92ed5
Merge branch 'origin/main' into feat/qqbot-streaming-improvements
Eric-GoodBoy-Tech Jul 1, 2026
d4ed4ab
fix(qqbot): store botOpenId per-group instead of global config mutation
Eric-GoodBoy-Tech Jul 1, 2026
f5c19c6
fix(qqbot): include msg_id in active retry to stay passive
Eric-GoodBoy-Tech Jul 1, 2026
a59916d
fix(qqbot): remove dead botOpenId field, TS6133
Eric-GoodBoy-Tech Jul 1, 2026
8869bf1
fix(qqbot): move isDuplicate after isAtBot guard in handleGroup
Eric-GoodBoy-Tech Jul 1, 2026
fdfd483
refactor(qqbot): extract shared prepareGroupMessage helper
Eric-GoodBoy-Tech Jul 1, 2026
772bdce
fix: remove duplicate audit:runtime:critical script entry
Eric-GoodBoy-Tech Jul 1, 2026
7751b90
fix: clean up botOpenIdByGroup on GROUP_DEL_ROBOT
Eric-GoodBoy-Tech Jul 1, 2026
a31b688
refactor: make sandbox required in fetchGatewayUrl
Eric-GoodBoy-Tech Jul 1, 2026
3be08b6
docs: remove inaccurate 2000-char message limit guidance
Eric-GoodBoy-Tech Jul 1, 2026
9c5ef1a
fix(qqbot): address 4 review threads from PR #5902
Eric-GoodBoy-Tech Jul 1, 2026
16f3f4d
fix(qqbot): update msgSeqMap after plain-text fallback succeeds
Eric-GoodBoy-Tech Jul 1, 2026
ff428d8
fix(qqbot): fix extractBotOpenId guard for member_openid-only mentions
Eric-GoodBoy-Tech Jul 1, 2026
6f1a6e7
refactor(qqbot): clean up QQChannel.ts guards and dead code
Eric-GoodBoy-Tech Jul 1, 2026
e81767e
fix(channels): /new bypasses shared session confirmation
Eric-GoodBoy-Tech Jul 1, 2026
04802bd
Revert "fix(channels): /new bypasses shared session confirmation"
Eric-GoodBoy-Tech Jul 1, 2026
5308d28
fix(qqbot): address adversarial review — pendingStreamDelete cleanup,…
Eric-GoodBoy-Tech Jul 1, 2026
73a33ef
fix(qqbot): add msg_seq to plain-text fallback body, fix pendingStrea…
Eric-GoodBoy-Tech Jul 1, 2026
939ff52
fix(qqbot): guard pendingStreamDelete by entry reference to prevent c…
Eric-GoodBoy-Tech Jul 1, 2026
3d5b00b
fix(qqbot): address wenshao R20 — flushedEntry guard in catch, isMent…
Eric-GoodBoy-Tech Jul 1, 2026
eb31791
fix(qqbot): address wenshao R21 — sendMessage rethrow, duplicate retr…
Eric-GoodBoy-Tech Jul 2, 2026
d1c4d36
fix(qqbot): use Record<string,unknown> instead of any for private mem…
Eric-GoodBoy-Tech Jul 2, 2026
7bddb17
fix(qqbot): restore throw e in sendMessage catch block
Eric-GoodBoy-Tech Jul 2, 2026
fe0d6a5
fix(qqbot): batch fix 19 review threads
Eric-GoodBoy-Tech Jul 2, 2026
bc7dcb3
fix(qqbot): batch fix 7 new review threads
Eric-GoodBoy-Tech Jul 2, 2026
728bc6d
fix(qqbot): batch fix 11 review threads
Eric-GoodBoy-Tech Jul 2, 2026
6bec36e
test(qqbot): add coverage for orphaned-buffer and onToolCall pendingS…
Eric-GoodBoy-Tech Jul 2, 2026
1779f8b
fix(qqbot): batch fix 11 review threads
Eric-GoodBoy-Tech Jul 2, 2026
cda183e
fix(qqbot): batch fix 5 review threads
Eric-GoodBoy-Tech Jul 2, 2026
365c316
fix(qqbot): batch fix all remaining review threads
Eric-GoodBoy-Tech Jul 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 53 additions & 26 deletions docs/superpowers/plans/2026-06-30-channel-loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
## Task 1: Core Exports And Store

**Files:**

- Modify: `packages/core/src/index.ts`
- Modify: `packages/channels/base/src/paths.ts`
- Create: `packages/channels/base/src/ChannelLoopStore.ts`
Expand Down Expand Up @@ -80,7 +81,9 @@ describe('ChannelLoopStore', () => {
runCount: 0,
createdAt: '2026-06-30T09:00:00.000Z',
});
await expect(store.listForTarget('telegram-main', target)).resolves.toHaveLength(1);
await expect(
store.listForTarget('telegram-main', target),
).resolves.toHaveLength(1);
});

it('enforces target quotas atomically through createForTarget', async () => {
Expand All @@ -100,32 +103,37 @@ describe('ChannelLoopStore', () => {
createdBy: 'Alice',
};

await expect(store.createForTarget(input, 1)).resolves.toMatchObject({ id: 'loop-1' });
await expect(store.createForTarget(input, 1)).resolves.toMatchObject({
id: 'loop-1',
});
await expect(store.createForTarget(input, 1)).resolves.toBeUndefined();
});

it('loads pre-lifecycle loop JSON with runCount defaulted to 0', async () => {
const dir = await mkdtemp(join(tmpdir(), 'channel-loop-legacy-'));
const filePath = join(dir, 'loops.json');
await writeFile(filePath, JSON.stringify([
{
id: 'loop-legacy',
channelName: 'telegram-main',
target,
cwd: '/repo',
cron: '0 9 * * *',
prompt: 'post summary',
recurring: true,
enabled: true,
createdBy: 'Alice',
createdAt: '2026-06-30T09:00:00.000Z',
consecutiveFailures: 0,
},
]));

await expect(new ChannelLoopStore({ filePath }).list()).resolves.toMatchObject([
{ id: 'loop-legacy', runCount: 0 },
]);
await writeFile(
filePath,
JSON.stringify([
{
id: 'loop-legacy',
channelName: 'telegram-main',
target,
cwd: '/repo',
cron: '0 9 * * *',
prompt: 'post summary',
recurring: true,
enabled: true,
createdBy: 'Alice',
createdAt: '2026-06-30T09:00:00.000Z',
consecutiveFailures: 0,
},
]),
);

await expect(
new ChannelLoopStore({ filePath }).list(),
).resolves.toMatchObject([{ id: 'loop-legacy', runCount: 0 }]);
});

it('refuses corrupt JSON instead of treating it as empty state', async () => {
Expand Down Expand Up @@ -334,7 +342,9 @@ export class ChannelLoopStore {
}
for (const [index, value] of parsed.entries()) {
if (!isChannelLoop(value)) {
throw new Error(`Invalid channel loop at index ${index} in ${this.filePath}.`);
throw new Error(
`Invalid channel loop at index ${index} in ${this.filePath}.`,
);
}
}
return parsed.map(normalizeLoop);
Expand Down Expand Up @@ -401,7 +411,8 @@ function isChannelLoop(value: unknown): value is ChannelLoop {
(loop['lastStatus'] === undefined ||
loop['lastStatus'] === 'ok' ||
loop['lastStatus'] === 'error') &&
(loop['lastError'] === undefined || typeof loop['lastError'] === 'string') &&
(loop['lastError'] === undefined ||
typeof loop['lastError'] === 'string') &&
typeof loop['consecutiveFailures'] === 'number' &&
(loop['runningSince'] === undefined ||
typeof loop['runningSince'] === 'string') &&
Expand Down Expand Up @@ -430,7 +441,11 @@ Export from `index.ts` and export cron helpers from `packages/core/src/index.ts`

```ts
export { ChannelLoopStore } from './ChannelLoopStore.js';
export type { ChannelLoop, ChannelLoopInput, ChannelLoopPatch } from './ChannelLoopStore.js';
export type {
ChannelLoop,
ChannelLoopInput,
ChannelLoopPatch,
} from './ChannelLoopStore.js';
export { channelLoopPath } from './paths.js';
export { nextFireTime, parseCron } from './utils/cronParser.js';
```
Expand All @@ -448,6 +463,7 @@ Expected: all tests pass.
## Task 2: Channel Loop Scheduler

**Files:**

- Create: `packages/channels/base/src/ChannelLoopScheduler.ts`
- Create: `packages/channels/base/src/ChannelLoopScheduler.test.ts`
- Modify: `packages/channels/base/src/index.ts`
Expand Down Expand Up @@ -509,6 +525,7 @@ Expected: all tests pass.
## Task 3: ChannelBase `/loop` Command Surface

**Files:**

- Modify: `packages/channels/base/src/ChannelBase.ts`
- Modify: `packages/channels/base/src/ChannelBase.test.ts`

Expand Down Expand Up @@ -541,8 +558,14 @@ Add:
```ts
export interface ChannelLoopController {
create(input: ChannelLoopInput): Promise<ChannelLoop>;
createForTarget?(input: ChannelLoopInput, maxEnabledLoops: number): Promise<ChannelLoop | undefined>;
listForTarget(channelName: string, target: SessionTarget): Promise<ChannelLoop[]>;
createForTarget?(
input: ChannelLoopInput,
maxEnabledLoops: number,
): Promise<ChannelLoop | undefined>;
listForTarget(
channelName: string,
target: SessionTarget,
): Promise<ChannelLoop[]>;
disable(id: string): Promise<boolean>;
validateCron(cron: string): void;
nextFireTime?(loop: ChannelLoop): Date;
Expand Down Expand Up @@ -611,6 +634,7 @@ Expected: all ChannelBase tests pass.
## Task 4: Loop Prompt Execution

**Files:**

- Modify: `packages/channels/base/src/ChannelBase.ts`
- Modify: `packages/channels/base/src/ChannelBase.test.ts`

Expand Down Expand Up @@ -671,6 +695,7 @@ Expected: all ChannelBase tests pass.
## Task 5: Adapter Opt-In

**Files:**

- Modify: `packages/channels/telegram/src/TelegramAdapter.ts`
- Modify: `packages/channels/telegram/src/TelegramAdapter.test.ts`
- Modify: `packages/channels/feishu/src/FeishuAdapter.ts`
Expand Down Expand Up @@ -715,6 +740,7 @@ Expected: all adapter tests pass.
## Task 6: CLI Startup Wiring

**Files:**

- Modify: `packages/cli/src/commands/channel/start.ts`
- Modify: `packages/cli/src/commands/channel/start.test.ts`

Expand Down Expand Up @@ -787,6 +813,7 @@ Expected: all CLI start tests pass.
## Task 7: Verification And PR

**Files:**

- Modify: `.qwen/pr-drafts/channel-loop.md`

- [ ] **Step 1: Run focused verification**
Expand Down
2 changes: 1 addition & 1 deletion docs/users/features/channels/qqbot.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export QQ_APP_SECRET=<your-app-secret>
"senderPolicy": "open",
"sessionScope": "user",
"cwd": "/path/to/your/project",
"instructions": "你是一个通过 QQ Bot 对话的 AI 助手。回复控制在 2000 字符以内。",
"instructions": "你是一个通过 QQ Bot 对话的 AI 助手。",
"blockStreaming": "on",
"groupPolicy": "disabled",
"groups": {
Expand Down
14 changes: 1 addition & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/channels/base/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ export type { SenderCheckResult } from './SenderGate.js';
export { SessionRouter } from './SessionRouter.js';
export {
sanitizeSenderName,
sanitizePromptText,
sanitizeLogText,
sanitizePromptText,
} from './sanitize.js';
export type {
Attachment,
Expand Down
4 changes: 4 additions & 0 deletions packages/channels/base/src/sanitize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ export function sanitizePromptText(text: string): string {
return (
text
.replace(PROMPT_UNSAFE_INVISIBLES, ' ')
// Strip injection-style bracket tags like [SYSTEM]: only at the
// start of a line (possibly indented with spaces/tabs) — not mid-
// sentence brackets like "see [docs] please". The m flag makes ^
// match after each line break.
.replace(/^([ \t]*)\[([^\]\r\n]{1,64})\](:?)/gm, '$1$2$3')
// Fold ASCII C0/DEL, including CR/LF/TAB, so attacker-controlled group
// text cannot create prompt lines outside the adapter's sender attribution.
Expand Down
6 changes: 3 additions & 3 deletions packages/channels/feishu/src/adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -867,9 +867,9 @@ describe('FeishuChannel', () => {
token: 'tenant-token',
expiresAt: Date.now() + 3600_000,
};
const fetchSpy = vi.spyOn(global, 'fetch').mockResolvedValue(
new Response('{}', { status: 200 }),
);
const fetchSpy = vi
.spyOn(global, 'fetch')
.mockResolvedValue(new Response('{}', { status: 200 }));

await channel.pushLoop(
{
Expand Down
Loading
Loading