- Mark @qwen-code/channel-plugin-example as private in package.json
- Remove publish step from release workflow
- Remove file: to semver rewrite logic in version script
- Use file: reference for @qwen-code/channel-base dependency
This change prevents the example plugin from being published to npm, as it's only intended for internal/development use.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add IS_SANDBOX environment detection
- Skip cron interactive tests when running in Docker sandbox
- Move timeout options inline with test definitions
- Add comment explaining flaky test workaround
This prevents flaky test failures in the Docker sandbox environment while preserving test coverage in local development.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Update all packages from 0.13.x to 0.14.0
- Update sandbox image URI to 0.14.0
This prepares the 0.14.0 release with updated version numbers
across all workspace packages.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Resolve punycode to userland package instead of deprecated node:punycode
built-in to avoid deprecation warnings
- Skip cron-tools env var test in sandbox mode since Docker containers
don't receive environment variables set in the test process
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
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