1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 Tom F. <tomf@tomtomtech.net> (https://github.com/tomtom215)
//! Pure decision helpers for the message send path.
//!
//! Free functions with no `self` and no I/O, split out of `mod.rs` so the send
//! path reads as a sequence of decisions rather than a sequence of decisions
//! interleaved with the predicates behind them. Each one is separately
//! testable, which matters because their edge cases (an aged token, a
//! still-in-use context lock) are exactly what a mutation test probes.
use Instant;
use truncate_history;
use CancellationEntry;
use Task;
/// Hard cap on the number of messages retained in `Task.history`.
///
/// Oldest messages are dropped first. Bounds per-task memory for
/// long-running multi-turn conversations; `GetTask`'s `historyLength`
/// further truncates what is returned to clients.
pub const MAX_TASK_HISTORY_MESSAGES: usize = 1024;
/// Shapes the history carried by a *send response* (or streaming snapshot)
/// per `SendMessageConfiguration.historyLength`.
///
/// The store always keeps the full (capped) history — this only governs the
/// response payload. The default (`None`) omits history entirely: the
/// sender already holds the message it just sent, and echoing it back
/// doubled response payloads for large sends (the 1 MiB benchmark tripped
/// the regression gate at +95% median). `Some(0)` also omits; `Some(n)`
/// keeps the `n` most recent messages, mirroring `GetTask` semantics.
pub
/// Returns the JSON-serialized byte length of a value without allocating a `String`.
pub
// ── Send-path decision helpers ────────────────────────────────────────────────
//
// Extracted from `send_message_inner` so the branch conditions are unit-testable
// in isolation (the enclosing async handler is not easily driven to these exact
// states).
/// A second `SendMessage` targeting a task that still has a **live**
/// (non-cancelled) cancellation token must be rejected: an executor is already
/// in flight for that `task_id`.
pub
/// Whether a non-cancelled cancellation token has aged at or past
/// `max_token_age` and is therefore a candidate for the stale-token sweep.
pub
/// Whether an aged token should actually be evicted: only when its event queue
/// is gone (the executor has finished). A token whose queue is still live is
/// kept so the running task stays cancelable.
pub const
/// Re-validates, under the write lock, that a sweep candidate is still
/// evictable at removal time.
///
/// Between the read-lock candidate collection and the write-lock removal, a
/// concurrent send can replace the entry with a **fresh, live** token for the
/// same task id (a cancel-then-resend race: the cancelled token passes the
/// in-flight check, and the resend inserts its own token). Removing by id
/// unconditionally would delete that live token and leave the resent executor
/// uncancelable for its whole run — so only entries that are *still* cancelled
/// or *still* aged are removed. A freshly-inserted token is neither.
pub