fix(cron): wire task-route lease into isolated cron delivery-target (closes #92460)#2
Closed
wangmiao0668000666 wants to merge 2386 commits into
Closed
fix(cron): wire task-route lease into isolated cron delivery-target (closes #92460)#2wangmiao0668000666 wants to merge 2386 commits into
wangmiao0668000666 wants to merge 2386 commits into
Conversation
* fix(agents): skip fallback auth gate for CLI runtimes * fix(agents): skip auth gate for CLI-owned transport * fix(agents): skip auth gate for CLI-owned transport --------- Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
…enclaw#88581) * Add /name chat command to rename the current session Adds a `/name <title>` slash command so users can name or rename the current session directly from any chat channel, instead of only through the web/admin session manager. This keeps parallel sessions easy to tell apart from within the chat flow. Behaviour: - `/name <title>` sets the session label, reusing the canonical `parseSessionLabel` validation (trim, non-empty, max 512 chars) and the same cross-store uniqueness rule enforced by the web `sessions.patch` path, so chat naming behaves identically to the session manager. - `/name` with no argument shows the current name plus a locally derived `deriveSessionTitle` suggestion without mutating anything (no LLM). - Only authorized senders can rename (rejectUnauthorizedCommand), matching /goal. The label surfaces everywhere sessions.list is shown (TUI, web, CLI, MCP). The handler resolves the session via resolveSessionStoreEntry so renames land on the canonical entry even when the store still holds a legacy or case-folded key alias, and excludes those aliases from the uniqueness scan to avoid false conflicts. Failed renames skip the store write. Registers the command in commands-registry.shared.ts and the handler in loadCommandHandlers, documents it in docs/tools/slash-commands.md, and adds unit tests covering rename, no-arg suggestion, duplicate-label rejection, unauthorized senders, disabled text commands, and persisted-name re-read. Part of the chat-native session naming feature (follows the web in-chat rename PR). Relates to openclaw#85502 and openclaw#54397. Co-authored-by: Copilot <[email protected]> * fix(name): seed native sessions and persist renames via canonical key Address Codex review on PR openclaw#88581: - Fall back to the in-memory params.sessionEntry when the store has no row yet, so a brand-new native slash session can be named from its first /name command instead of failing with 'no active session to name'. - Persist the rename through resolved.normalizedKey and drop legacy/ case-folded alias keys (mirroring persistResolvedSessionEntry) so the canonical entry is updated and sessions.list stops surfacing the stale alias row. Co-authored-by: Copilot <[email protected]> * fix(name): emit session metadata changes Route successful /name renames through the shared command session metadata seam so subscribed session lists receive sessions.changed like /goal. Co-authored-by: Copilot <[email protected]> * feat(commands): add /name to rename the current session from chat * fix(docs): document the /name slash command --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Agent <[email protected]> Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
Dependency GuardThis PR changes dependency-related files. Maintainers should confirm these changes are intentional. Changed files:
Maintainer follow-up:
|
Dependency graph changes notedThis PR includes dependency graph changes. The dependency guard is informational because the PR author is a repository admin or a member of
Security review is still recommended before merge when the dependency graph change is intentional. |
wangmiao0668000666
force-pushed
the
fix/92460-task-route-lease
branch
from
June 19, 2026 15:47
55baaa7 to
3536811
Compare
wangmiao0668000666
added a commit
that referenced
this pull request
Jun 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes openclaw#92460 — isolated cron completion delivery drops explicit
delivery.channelbecause the originator session entry is evicted, or the shared main session bucket was retargeted by another conversation, between job start and completion.This PR introduces a generic task-route lease module keyed by detached run id, and wires the isolated cron completion path through it as a session-identity fallback.
Why
The detached-task completion delivery path (cron, subagent, ACP, codex) resolves its outbound origin at completion time. Today:
lastChannel/lastTovalues can be a different conversation's room by the time the cron completes.deliveryconfig injob.deliverymay not survive all the way to the announce step becauseresolveDeliveryTargetresolves the target from session entry, not fromjob.deliverydirectly.When the resolver falls through to the shared main bucket, the cron is either drained to the wrong room (cross-conversation leak) or refused by the openclaw#91613 inherited-room check. Either way the cron completion delivery is lost.
Solution
A SQLite-backed task-route lease row keyed by
runId:createRunningTaskRunfor the generic case, and explicitly intryCreateCronTaskRunfor cron because cron setsdeliveryStatus: "not_applicable"which the auto-hook skips).requesterOriginis aDeliveryContextcapturing the cron job's owndeliveryconfig.resolveDeliveryTargetas a session-identity fallback, rankedstoredDeliveryEntry ?? threadEntry ?? leaseEntry ?? mainEntry.tryFinishCronTaskRun) so it can be GC'd instead of waiting for TTL expiry.expireStaleTaskRouteLeases, available to gateway startup / a low-frequency timer.The auto-settle hook in
setDetachedTaskDeliveryStatusByRunId(added in this PR) retires the lease on any terminalTaskDeliveryStatustransition (delivered/session_queued→ settled;failed→ retired).Changes
PR-A — lease module (commit 50711b7)
src/state/openclaw-state-schema.sql— newtask_route_leasestable (PRIMARY KEY run_id, with status/expires_at/indexes; no FK to task_runs because leases are created before the parent task_runs row in the cron flow)src/state/openclaw-state-schema.generated.ts+src/state/openclaw-state-db.generated.d.ts— generated Kysely typessrc/tasks/task-route-lease.ts— public API:acquireTaskRouteLease,getActiveTaskRouteLease,settleTaskRouteLease,mapDeliveryStatusToLeaseRetirement,extendTaskRouteLease,expireStaleTaskRouteLeases. Best-effort: never throws.src/tasks/task-executor.ts— auto-acquire oncreateRunningTaskRun(skips whendeliveryStatus: "not_applicable"); auto-settle onsetDetachedTaskDeliveryStatusByRunIdfor terminal delivery statusessrc/tasks/task-route-lease.test.ts— 9 unit tests (acquire/getActive/settle/extend/expire/persist/re-acquire/map)scripts/repro/issue-92460-task-route-lease-lifecycle.mts— 7-step real-DB reproPR-B — isolated cron wiring (commit 55baaa7)
src/cron/isolated-agent/delivery-target.ts— read lease viagetActiveTaskRouteLease(jobPayload.runId); precedence chainstoredDeliveryEntry ?? threadEntry ?? leaseEntry ?? mainEntry;usedSharedMainFallbackexcludesleaseEntryso [Bug] Gateway reply-queue state not flushed on restart — stale queued replies drain to wrong Matrix room after restart openclaw/openclaw#91613 inherited-room refusal does not fire on lease originsrc/cron/isolated-agent/run.ts—RunCronAgentTurnParamsandresolveCronDeliveryContextacceptrunId; plumbed toresolveDeliveryTarget({ runId, ... })src/cron/service/state.ts—runIsolatedAgentJobdep acceptsrunIdsrc/cron/service/timer.ts—ExecuteJobCoreOptionsacceptsrunId; forwarded throughexecuteJobCoreWithTimeout→executeJobCore→state.deps.runIsolatedAgentJobsrc/cron/service/task-runs.ts—tryCreateCronTaskRunexplicitly acquires the lease (auto-hook is skipped fornot_applicabledelivery);tryFinishCronTaskRunexplicitly settles (success →settled, failure →retired); new helperresolveCronLeaseRequesterOriginderives aDeliveryContextfromjob.deliverysrc/gateway/server-cron.ts—runIsolatedAgentJobimplementation forwardsrunIdtorunCronIsolatedAgentTurnsrc/cron/isolated-agent/delivery-target.issue-92460.test.ts— 5 unit tests (lease origin wins over empty/stale main; lease takes lower precedence than stored context and thread session; [Bug] Gateway reply-queue state not flushed on restart — stale queued replies drain to wrong Matrix room after restart openclaw/openclaw#91613 interaction)scripts/repro/issue-92460-cron-completion-leases.mts— 6-step real-DB reproReal behavior proof
Behavior addressed: an isolated cron whose originating session entry was evicted (or whose shared main session bucket was retargeted by another conversation) before completion fires still delivers to the cron job's own
delivery.channel/delivery.tovia the task-route lease, instead of being drained to the wrong room or refused by the [Bug] Gateway reply-queue state not flushed on restart — stale queued replies drain to wrong Matrix room after restart openclaw/openclaw#91613 inherited-room check.Real environment tested: Linux 4.19, Node 22 (per repo minimum), real on-disk SQLite via
openOpenClawStateDatabase({ env: { OPENCLAW_STATE_DIR: stateDir } })against a temp directory created withfs.mkdtempSync.Exact steps or command run after this patch:
pnpm exec tsx scripts/repro/issue-92460-task-route-lease-lifecycle.mts— 7 PASS linespnpm exec tsx scripts/repro/issue-92460-cron-completion-leases.mts— 6 PASS linesnode scripts/run-vitest.mjs src/tasks/task-route-lease.test.ts— 9 tests passednode scripts/run-vitest.mjs src/cron/isolated-agent/delivery-target.issue-92460.test.ts— 5 tests passednode scripts/run-vitest.mjs src/cron/ src/tasks/— 1566/1569 tests passed (the 3 failures are pre-existing flakes indetached-task-runtime.test.tsvi.spyOn(Date, "now")patterns, unrelated to this PR — same 33 failures appear on main without this PR)pnpm tsgo:core— exit 0pnpm build— exit 0node scripts/run-oxlint.mjs <modified files>— exit 0Evidence after fix (lease lifecycle repro, redacted temp dir path):
Evidence after fix (cron completion repro):
Observed result after fix: the lease is correctly acquired at cron job start, the resolver reads the captured
requesterOriginwhen session-key lookups miss, the lease is settled on terminal run status, and the row survives SQLite close+reopen (proves it lives in the shared state DB, not in-memory).What was not tested: this PR does not exercise the live Telegram bot or webchat in-process channel. The end-to-end send path is gated by the resolver's
ok: truereturn (proved by the unit tests), and the lease-only path is proven end-to-end against a real on-disk SQLite. A live channel run was not feasible in this environment.Verification
pnpm tsgo:core→ exit 0pnpm build→ exit 0node scripts/run-oxlint.mjs <modified files>→ exit 0node scripts/run-vitest.mjs src/cron/ src/tasks/→ 1566 passed, 3 pre-existing flakes unrelatedAI assistance
AI-assisted (Claude). Real-environment repro scripts were run by the human on a clean checkout of the branch; unit tests, typecheck, lint, and build all pass on Linux Node 22. Code is read-and-understood: the design was a joint effort with @vincentkoc's openclaw#92580 close comment as the design constraint (R1-R4), the cron side is wired to mirror the existing
tryCreateCronTaskRun/tryFinishCronTaskRunpattern, and thedeliveryStatus: "not_applicable"skip in the auto-hook is the reason cron needs the explicit acquire/settle.Related