Replace Telegraf with Grammy as the Telegram Bot framework.
- Replace @telegraf/types with @grammyjs/types in package-lock.json
- Swap telegraf dependency for grammy ^1.41.1 in package.json
- Update TelegramAdapter.ts: Bot instead of Telegraf, .api.* instead
of .telegram.* calls, .start() instead of .launch(), adjusted event
subscription syntax (message:text, message:photo, message:document)
Grammy is a more modern and actively maintained Telegram bot framework
for Node.js, improving reliability and reduce legacy dependencies.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add plugin-example to build order in scripts/build.js
- Add prepublishOnly script to auto-build before npm publish
This ensures the plugin-example package is built during the main build process and automatically compiled before publishing to npm.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
The previous version (1.1.0) has a native-level bug on macOS where each
PTY spawn leaks one /dev/ptmx file descriptor that is never closed. Over
a long session with hundreds of shell commands, this exhausts the
system-wide PTY pool (kern.tty.ptmx_max = 511), breaking other programs
like tmux and new terminal windows.
Root cause: microsoft/node-pty#882
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Previously, only shell scripts (.sh) had .claude -> .qwen path replacement.
Markdown files (.md) like cancel-ralph.md and help.md were missing this
conversion, causing incorrect paths like .claude/ralph-loop.local.md.
Now performVariableReplacement also replaces .claude directory references
in markdown files using the same regex pattern as shell scripts.
Root cause: PR #1835 accidentally overwrote PR #1912's correct telemetry
isolation during a merge conflict resolution. This restores the original
guard logic so subagent GeminiChat instances (which don't receive a
telemetryService param) no longer write to the global uiTelemetryService.
- Remove unused uiTelemetryService import from geminiChat.ts
- Guard telemetry calls with this.telemetryService checks
- Add test verifying subagent isolation
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
When the WASM files cannot be found (e.g. symlinked CLI whose vendor path
probe still fails, or any other I/O error), isShellCommandReadOnlyAST and
initParser previously left the agent in a permanently broken state:
- initParser() returned a rejected promise stored in initPromise, so every
subsequent call returned the same rejection immediately
- isShellCommandReadOnlyAST() propagated the error to its caller, which
caused the shell tool to throw and left the agent 'thinking forever'
Changes:
- Import isShellCommandReadOnly from shellReadOnlyChecker.js
- Track parserInitFailed: boolean flag on the parser singleton
- initParser(): on any init error, set parserInitFailed=true and reset
initPromise=null (preventing permanent hang on the rejected promise)
- isShellCommandReadOnlyAST(): check parserInitFailed before attempting
AST parse; wrap the AST path in try/catch so any unexpected runtime
error also falls through to the regex checker
- _resetParser(): also resets parserInitFailed so tests can re-initialise
- Export _setParserFailedForTesting() to allow unit tests to exercise the
fallback path without breaking actual WASM loading
Tests added:
- Fallback suite: verifies that when the parser is marked as failed, all
isShellCommandReadOnlyAST calls delegate to isShellCommandReadOnly and
that _resetParser() restores normal AST operation
- Consistency suite: 40+ commands from shellReadOnlyChecker.test.ts run
through BOTH implementations; every case must agree. Known intentional
divergences (pure assignment, process substitution, control flow,
function definitions) are documented explicitly - all happen to agree
on the same boolean result, confirming the two implementations are
behaviourally consistent across the tested corpus
Fixes#2758
The previous fix used fs.realpathSync(fileURLToPath(import.meta.url)) to
resolve symlinks before computing the WASM path. This works in the common
case (/usr/bin/qwen → /usr/lib/node_modules/.../cli.js), but silently falls
back to the unresolved symlink path when:
- realpathSync throws (ENOENT, EACCES, ELOOP, or other OS-level errors)
- Node.js has already resolved import.meta.url to the real path on some
platforms (making realpathSync a no-op and the first candidate correct),
but the vendor files are still not found at that location
The new approach for the bundle case (inSrcUtils = false) collects up to four
candidate directories:
1. path.dirname(fileURLToPath(import.meta.url)) — already resolved on
Node.js 18+ on most platforms
2. path.dirname(realpathSync(import.meta.url)) — symlink-resolved fallback
3. path.dirname(process.argv[1]) — entry point as invoked
4. path.dirname(realpathSync(process.argv[1])) — resolved entry point
It then calls fs.existsSync on each candidate path for the requested .wasm
file and returns the first one that actually exists, rather than trusting
that a single path computation is correct. If none exist we fall back to
the first candidate so the caller still gets a deterministic ENOENT (instead
of silently using a wrong path).
Fixes#2758
- fix: sanitize remote filenames with basename() and isolate uploads
in UUID subdirs to prevent path traversal and collision (#2-4, #27)
- fix: use crypto.randomInt() for pairing codes instead of Math.random() (#5)
- fix: pass config.sessionScope instead of hardcoded 'user' (#6);
add per-channel scope overrides via setChannelScope() for startAll (#7)
- fix: removeSession now returns removed session IDs and persists
when chatId is provided (#8)
- fix: /clear only removes the cleared session from instructedSessions,
not all sessions (#9)
- fix: DingTalk @mention stripping now removes only the first mention
instead of all mentions (#10)
- fix: remove dead TELEGRAF_COMMANDS Set and its guard (#13)
- fix: WeChat cursor saved after message processing, not before (#14)
- fix: crash recovery uses time-window counting instead of resettable
counter to prevent infinite restart loops (#17)
- fix: call channel.disconnect() before exit on crash exhaustion (#18)
- Architecture overview with platform adapters and ACP bridge
- Plugin system contract and extension loading
- Implementation guides for Telegram, WeChat, DingTalk
- Testing guide with mock servers and E2E scenarios
- Feature roadmap and known limitations
These docs provide the foundation for the external messaging integrations.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Scan a window of minutes around current time to find matching cron minutes
- Track matched cron minute in lastFiredAt instead of wall-clock time
- Add tests for positive jitter (recurring) and negative jitter (one-shot)
This ensures jittered jobs fire correctly when their delayed or advanced
fire time falls outside the original matching minute.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add queue-based cron processing in nonInteractiveCli for sequential execution
- Block cron processing while user prompt is active in Session
- Drain cron queue after prompt completion to process queued jobs
- Reduce recurring task auto-expiry from 7 days to 3 days
This fixes race conditions where cron jobs could fire during active prompts
and ensures cron prompts are processed sequentially.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Strip auth token when following redirects to a different host
- Strip auth token when downloading tarballs from a different host
- Fix package name encoding to replace all slashes, not just the first
This prevents credentials from being sent to unintended hosts when
private registries redirect to CDNs or other external domains.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Adds missing isCronEnabled mock function to the test configuration
to support the cron feature implementation.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add cron queue and scheduler state management to Session class
- Handle cron cancellation on user prompt and cancelPendingPrompt
- Start cron scheduler after prompt execution completes
- Drain cron queue sequentially to prevent concurrent chat access
- Execute cron prompts with proper message echoing and tool handling
- Add integration test for cron firing and sessionUpdate streaming
This enables cron jobs created during an ACP session to fire in the
background and stream results back to the client via sessionUpdate
notifications, even after the originating prompt has returned.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Model IDs can legitimately contain colons (e.g., `gpt-4o:online`).
Previously, any string with a colon was parsed as `authType:modelId`,
which caused valid model IDs with colons to be rejected.
Now, if the prefix before a colon isn't a known AuthType, the entire
string is treated as a bare model ID.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Strip trailing orphaned funcCall (force=true) before split point calculation,
so normal compression logic runs cleanly on the remaining history instead of
requiring ad-hoc special-casing
- Remove redundant lastToolCompletionSplitPoint machinery: after fixing the
i+2 index bug, lastSplitPoint already subsumes it, making Math.max redundant
- Add MIN_COMPRESSION_FRACTION constant (0.05) to guard against futile API
calls when historyToCompress is too small relative to total history
- Add tests for orphaned funcCall handling (force=true compresses, force=false NOOP)
- Add test for MIN_COMPRESSION_FRACTION guard
Fixes#2647
Add comprehensive tests for buildAgentContentGeneratorConfig and
resolveCredentialField, covering same-provider overrides, cross-provider
credential clearing, registry-resolved models, and edge cases.
This ensures correct model selection behavior for subagent configurations.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Handle bare model IDs by inheriting parent's authType
- Create dedicated ContentGenerator for any explicit model selection
- Add tests for model override scenarios
Previously, only cross-provider prefixed models (e.g., "openai:gpt-4o")
triggered ContentGenerator creation. Bare IDs like "qwen-coder" were
ignored, causing subagents to always use the parent's model.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Introduce SendMessageType.Cron to differentiate cron-triggered prompts
from user queries
- Skip UserPromptSubmit hook for cron messages
- Add getExitSummary() to display active loops when session ends
- Add tests for exit summary functionality
This improves cron loop handling by treating scheduled prompts
differently from user-initiated queries and provides better UX
when sessions end with active loops running.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Extract buildAgentContentGeneratorConfig to shared module for reuse
- Create per-agent ContentGenerator when subagent uses different provider
- Remove validation restriction on cross-provider model selectors
- Update tests to accept cross-provider selectors
This allows subagents to use models from different providers (e.g.,
"openai:gpt-4" when parent uses Qwen) by creating dedicated
ContentGenerators with proper auth credentials.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>