Prevent turns in noninteractive Cron sessions - #1547
Open
lihongguang-0014 wants to merge 36 commits into
Open
Conversation
(cherry picked from commit 8f9a959)
lihongguang-0014
force-pushed
the
fix/noninteractive-cron-send
branch
from
September 3, 2026 03:23
7f64b19 to
2a00df1
Compare
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.
Scope
Prevent isolated Cron sessions from accepting follow-up turns through either the Gateway or the Web UI.
sessions.pending_inputs.enqueueproves an exact queued-row or dispatch-receipt replay before Cron policy, while fresh canonical and stored legacy Cron requests fail withSESSION_NOT_INTERACTIVEbefore attachment staging or queue persistence./chat/newrecovery skips policy lookup only for a genuine provisional draft; recovered durable sessions page the authoritative directory even when the route initially has no session, and terminal misses or errors remain fail-closed.sessions.sendreplays an existing idempotency receipt first, then rejects a canonicalcron:key or stored noninteractive Cron session withSESSION_NOT_INTERACTIVE,accepted=false, andretryable=falsebefore annotation, attachment, message, intent, task, or runtime side effects; missing canonical keys cannot usenew_chatto create a writable session.sessions.steer.v2replays an exact durable receipt before policy, then rejects fresh canonical or stored legacy Cron input before prepare/admission; legacysessions.steerrejects before runtime or transcript persistence, and delegatedsessions.pending_inputs.steerretains rejected queue rows.cron:identity remains authoritative over delivery metadata.interactive=falseorsessionKind=crondirectory rows as read-only, with the exact, case-sensitive canonicalcron:namespace as the compatibility fallback for old Gateways and deep links. It replaces the composer with a localized read-only status, removes turn-mutating actions, and blocks their handlers plus central send, message-mutation, and hidden attachment paths without capturing uppercase custom keys. Meta inspection, expansion, and dismissal remain available.set/edit/resumeand initial-routing send validation honor exact durable receipts before live Cron/routing checks. WebUI direct, queued follow-up, real queue drain, and Steer recovery replay immutable acceptance-unknown requests before mutable attachment, image/routing, annotation, or command-capability admission.chat.sendfollow-up cannot clear a compaction marker owned by an already-running Cron turn. Context shaping belongs to TurnRunner, so the compatibility wrapper does not clear session-keyed compaction state on policy rejection or earlier validation failures.Root cause
The session projection classified isolated Cron runs as
interactive=false, but turn and Goal admission did not consistently enforce that classification after receipt resolution. ChatView also had no selected-session capability seam and retained mutable input paths for a Cron route. Finally, some exact client replays consulted mutable local admission state before sending the original idempotent request back to Gateway.Non-goals
interactive=falsefor CLI, channel, subagent, or unknown sessions.Compatibility
cron:keys are read-only in the client.cron:identity.Platform impact
The report was reproduced on Windows 11, but the affected Gateway admission and browser input paths are platform-neutral. The fix adds no OS-specific behavior.
Tests
uv run pytest -q tests/test_gateway/test_rpc_sessions.py tests/test_gateway/test_turn_ingress_rpc.py tests/test_gateway/test_rpc_cron_current_session.py tests/test_gateway/test_websocket_request_concurrency.py tests/test_application/test_session_lifecycle.py tests/test_gateway/test_session_lifecycle_adapter.py tests/contracts/test_sessions_lifecycle_contract.py— 476 passed after mergingorigin/mainatf4e65ef26.npm exec -- vitest run --maxWorkers=1— 428 files, 5,283 tests passed on fixed head2a00df133f02279a03a643e82329b9516e719f29.npm exec -- vitest run src/composables/chat/useChatSessionInteractivity.test.ts src/views/ChatView.cron-read-only.test.ts --maxWorkers=1— 2 files, 23 tests passed on final HEAD.uv run pytest -q tests/test_gateway/test_goal_rpc.py tests/test_gateway/test_turn_ingress_intents.py— 98 passed on final HEAD; Goal Cron rejection/replay and live-routing-change receipt replay are covered.uv run pytest -q tests/test_gateway/test_turn_ingress_rpc.py— 79 passed on final HEAD.npm run test:unit -- src/composables/chat/useChatSend.attachments.test.ts— 258 passed on final HEAD, including the production queue drain, ordinary Retry routing, and direct composer exact-replay state through changed live admission.npm run typecheck— architecture, RPC architecture, chat security, theme, motion/radius, i18n parity for all six locales, and TypeScript checks passed on final HEAD.git diff --checkpassed.npm run build,uv build --wheel, anduv run opensquilla --help.Manual / live limitations
No credentialed provider, live browser, or scheduled wall-clock Cron run was performed. The changed paths are covered with deterministic offline Gateway and Web tests; CI remains authoritative for the repository-wide matrix.
Release note
Release note: NONE
Safety
No secrets, generated Web artifacts, local paths, private prompts/transcripts, or channel identifiers are committed.
Commit Lineage
This is a build-on-existing remediation of the eligible first-time-contributor PR #1528.
8f9a9593333fd6e253dfae15360c76fc4677fef1bymikemikimike <13286568797@163.com>.909f6777e646e9b2311f4c5f5f6c36ded8166a2d, cherry-picked with-xand preserving author/message.97f6b4dc34e828368709a2f0e2ab282b58ce3329.b5bb68691126e9b6c30717e8e0849eab2e7dbaee.333f14c11889d0b6dedb85e82bcdc9cef976f208.ce32c22569aed1ea97b32a9314ff50fe49a1aa05.23a82da7e6a5b6c975d036ff1ffd3fba8be12bfd.e531651c69719831c8d895d602af5590016d723d.26c431df77edfdd8739de678a47dab5c20259763.90cfc6aad343e348696690f0d326c09d6d67271b.1164f5961b562aadd2a2a94e18dff6d494360918, includingorigin/mainatf4e65ef26cb2bb190dd9e6910027f571effa4922.822bacdb2145fd1077e77e7cdbdf414edd8f5971.0d0093eb1b2eb03e80af91af44675e555e8bdbe4.2de8c39b226831f84175d2acf6de304b9346739f.3e749132489257b6babee7f63ae143f0f885b6a4.ddedb4ab5e3c93e012a97e048e841285bb390545.285109b1e1c833467498831166f09e6a4f7d25e5.33a3d5339aebd7446e8b351e0868b88526e2e44f.cd70aa0c40d4b027c4f046cb2676bef9ba1ea3e8.a16e1aeccd68a75b4ee9a9f82e025bf1c209768f.9be47fe3cd2f88b8aef4a7bbb27fa7470c437757.63493da42c3e897b2339ea5e1fb6fceda5ab6f05.bdef53fc8cb877655781743268ccd41e7e505c67.896eb34c48a3575b31d65ca6f9538442072c8598.bbfa6dd3ee5e6d14060e74c412da268f914278d3.53c1698bf14e46d5bae8c5e87922cb42296edf0c.fa01e57f118e2dbf4aee8bc07de8812a9a94e9dd.ba48697ae06fd5ae23f8f86db38fd4202eb798d5.\n- Twentieth independent-review corrections:2a00df133f02279a03a643e82329b9516e719f29.\nThe contributor branch and PR were not modified.
Linked issue
Fixes #1520