mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-10 17:27:10 +00:00
* feat(loop): add second-resolution session wakeup engine
Add a session-scoped wakeup primitive for self-paced /loop, aligned with
Claude Code's ScheduleWakeup. An independent, second-resolution channel in
CronScheduler — separate from cron jobs (never durable, not counted against
MAX_JOBS, fired at an exact time, not minute-rounded):
- scheduleWakeup(delaySeconds, prompt): clamps to [60, 3600]s (1200s default
for non-finite input); returns {scheduledFor, clampedDelaySeconds, wasClamped}.
- Fires through the existing onFire channel and counts toward sessionSize, so
there are no cli delivery-path changes and a pending wakeup holds a headless
run open — re-arm keeps the loop alive, omitting the call ends it.
- cancelWakeup / cancelAllWakeups primitives (for loop-scoped cancellation).
- loop_wakeup tool: delaySeconds schema, structured clamp output, cache-window
picking guidance, verbatim /loop prompt, reason shown to the user, and the
"call to keep alive / omit to end" contract — all mirroring ScheduleWakeup.
getDefaultPermission stays 'ask' (out of SAFE_TOOL_ALLOWLIST) so AUTO still
routes scheduling future model input through the classifier, like CronCreate.
Closes #5156
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(loop): tighten session wakeup lifecycle
* fix(loop): address wakeup review nits
* feat(loop): wire prompt-only /loop to self-paced wakeups
Make `/loop <prompt>` (no interval) a self-paced loop in the bundled loop
skill: run the prompt immediately, then schedule at most one future
continuation via loop_wakeup (delaySeconds) — no recurring cron.
- Three explicit paths: prompt-only self-paced (LoopWakeup), fixed-interval
recurring (CronCreate), and list/clear management (CronList/CronDelete).
- The continuation uses delaySeconds (aligned with the second-resolution
wakeup engine) and re-feeds `/loop ${original prompt}` verbatim to re-enter
the skill; the model re-arms only when a further check is useful.
- Adds loop_wakeup to the skill's allowedTools.
- Static SKILL contract tests, including delaySeconds (not delayMinutes).
Closes #5184
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(loop): clarify wakeup delay reporting
* fix(loop): make wakeups manageable
* fix(loop): address wakeup review feedback
* fix(loop): clarify wakeup management wording
* fix(loop): clarify wakeup continuation tooling
* fix(loop): bound self-paced wakeup chains
* fix(loop): distinguish wakeup fires in lists and UI
* test(loop): cover wakeup labels in cli paths
* fix(loop): enforce session wakeup chain limit
* fix(loop): align wakeup delay metadata
* fix(loop): handle stopped wakeup scheduling
* fix(loop): make the wakeup chain limit a true session-level budget
The 24h chain limit reset `wakeupChainStartedAt` whenever `wakeups`
emptied — on every fire and on cancel. Because a self-paced loop leaves
at most one pending wakeup, each fire emptied the map and restarted the
clock, so a continuous re-arming loop never reached the cap (and a
cancel-then-reschedule could reset it too).
Reset the chain clock only on stop()/destroy() (a new session): the 24h
budget now spans the whole session, bounds continuous re-arming, and
closes the cancel bypass. Tests cover the clock persisting across fires,
cancel not resetting it, and stop starting a fresh budget.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(loop): correct CronList durability wording and bound wakeup prompt
Address two review suggestions on #5197:
- CronList tool description said cron jobs and loop wakeups are "both
session-only and durable", implying wakeups can be durable. Loop
wakeups are always session-only; only cron jobs can be durable.
Reword so the model isn't misled into expecting durable wakeups.
- LoopWakeup `prompt` had no maxLength, unlike sibling tools
(task-create, send-message). Add maxLength: 10000 to bound the
model-generated continuation prompt.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(loop): let the first wakeup arm before the scheduler starts
Critical (wenshao, #5197): LoopWakeup hard-rejected when
`scheduler.running` was false, but on the first self-paced /loop in a
session with no cron jobs the scheduler hasn't started yet
(#startCronSchedulerIfNeeded bails on !hasPendingWork). The post-prompt
hook starts the tick *after* the turn, once a wakeup exists — so the
guard rejected the very call that makes the loop possible, breaking the
primary use case.
The `running` check was a proxy for "cron is alive", added to reject
re-arms after the token-limit breaker. Replace it with an explicit,
permanent `disabled` state so the two cases are distinguishable:
- CronScheduler gains `disabled` + `disable()` (sets the flag, stops).
- LoopWakeup rejects only when `scheduler.disabled`, not when merely
stopped — a stopped-but-restartable scheduler still accepts wakeups.
- The token-limit breaker calls `disable()` instead of `stop()`, so its
rejection (the original intent) is preserved.
Also attribute cron-prompt errors by source: `[loop error]` vs
`[cron error]` (item.source was already in scope).
Tests: reject-when-disabled, schedule-when-stopped (the regression),
and a disable() unit test.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(loop): clear the pending wakeup when a re-arm exceeds the 24h budget
Critical (round-4 review, #5197): scheduleWakeup() threw the 24h-limit
error *before* clearing the prior wakeup, so a rejected re-arm left the
previous wakeup in the map. Its fireAtMs is now in the past, so the next
tick fires it — one iteration past the budget it's meant to cap. A test
even codified this (sessionSize === 1 after a rejected re-arm).
Production can't actually reach it (the 1s tick fires the wakeup at
~+3600s, and a stopped scheduler clears wakeups), but the safety budget
should hold unconditionally. Clear the pending wakeup up front, before
the budget check, so a rejected re-arm leaves nothing behind. Update the
test to assert no wakeup remains.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(loop): update Session token-limit test for the disable() breaker
CI regression from the disable() refactor (
|
||
|---|---|---|
| .. | ||
| adaptive-output-token-escalation | ||
| auth | ||
| auto-memory | ||
| channels | ||
| compact-mode | ||
| compaction-image-stripping | ||
| customize-banner-area | ||
| daemon-acp-http | ||
| daemon-transport-abstraction | ||
| fork-subagent | ||
| prompt-suggestion | ||
| rt-optimization | ||
| session-idle-reaper | ||
| session-recap | ||
| session-title | ||
| skill-nudge | ||
| slash-command | ||
| structured-output | ||
| tool-use-summary | ||
| virtual-viewport | ||
| 2026-05-15-async-memory-recall-design.md | ||
| auto-compaction-threshold-redesign.md | ||
| custom-api-key-auth-wizard-prd.md | ||
| daemon-idle-detection-api.md | ||
| f2-mcp-transport-pool.md | ||
| markdown-syntax-extension.md | ||
| openrouter-auth-and-models.md | ||
| telemetry-llm-request-timing-design.md | ||
| telemetry-outbound-propagation-design.md | ||
| telemetry-resource-attributes-design.md | ||
| telemetry-subagent-spans-design.md | ||
| workflow-tracing-gaps.md | ||
| worktree.md | ||