1
00:00:00,000 --> 00:00:03,360
Hey everyone, this is The Codex Changelog.

2
00:00:03,394 --> 00:00:06,880
Thanks to Jellypod for helping make this
daily show a reality.

3
00:00:07,027 --> 00:00:12,480
Today's topic is a pull request that
landed on main on October 7: number

4
00:00:12,640 --> 00:00:19,360
51734, commit 2dae757, titled Load
persisted

5
00:00:19,429 --> 00:00:21,840
sender context for Guardian reviews.

6
00:00:22,040 --> 00:00:28,880
The 0.162.0 alpha builds are being cut
right now, and the latest stable release is

7
00:00:28,920 --> 00:00:31,680
still 0.160.1.

8
00:00:32,088 --> 00:00:36,708
Quick setup for anyone who skipped our
episode on 0.153.0.

9
00:00:37,368 --> 00:00:42,288
Guardian is the security reviewer that
looks at what an agent wants to do and checks

10
00:00:42,328 --> 00:00:44,128
it against what a real user asked for.

11
00:00:44,768 --> 00:00:48,788
In that release, Guardian context moved
into a persistent review store,

12
00:00:49,188 --> 00:00:53,548
so token compaction inside one session
wouldn't cause what we called security

13
00:00:53,628 --> 00:00:54,088
amnesia.

14
00:00:54,688 --> 00:00:58,308
Today's change is the same problem,
stretched across machines.

15
00:00:58,828 --> 00:01:03,188
The PR states it plainly: Guardian cannot
capture sender instructions from another

16
00:01:03,248 --> 00:01:07,328
host when the sender thread is absent from
the receiver's local runtime.

17
00:01:07,625 --> 00:01:13,145
Picture an agent on host A delegating a
subtask to an agent on host B.

18
00:01:13,273 --> 00:01:18,025
When the task arrives, Guardian on B wants
proof of who asked for what.

19
00:01:18,094 --> 00:01:21,865
Before this change, it looked for the
sender thread in B's local runtime.

20
00:01:22,052 --> 00:01:25,545
If that thread wasn't loaded there,
Guardian had nothing.

21
00:01:25,758 --> 00:01:30,985
So a perfectly legitimate delegated task
could look like it had no authorization

22
00:01:31,042 --> 00:01:31,625
behind it.

23
00:01:31,778 --> 00:01:38,758
The fix is in codex-rs core, mainly
sender_context.rs, with smaller edits

24
00:01:38,785 --> 00:01:45,318
to guardian_sender_messages.rs and
rollout_reconstruction.rs.

25
00:01:45,585 --> 00:01:50,598
If the sender thread isn't loaded locally,
Codex now falls back to the authenticated

26
00:01:50,644 --> 00:01:51,398
thread store.

27
00:01:51,611 --> 00:01:57,798
It calls load_latest_model_context with
the source thread ID and

28
00:01:57,868 --> 00:01:59,878
include_archived set to true.

29
00:02:00,038 --> 00:02:06,198
Then it rebuilds the sender's history
using ContextManager reconstruct_rollout,

30
00:02:06,238 --> 00:02:10,838
which replays stored history without
starting a session or acquiring its writer.

31
00:02:10,918 --> 00:02:12,998
The sender never gets resumed.

32
00:02:13,260 --> 00:02:16,500
I want to pause on what the reconstruction
refuses to trust,

33
00:02:16,680 --> 00:02:18,600
because that's where the security thinking
is.

34
00:02:19,140 --> 00:02:24,000
First, it only accepts the stored history
if the SessionMeta record matches the

35
00:02:24,100 --> 00:02:27,580
source thread ID and the source isn't
internal.

36
00:02:28,540 --> 00:02:32,400
Second, tool outputs get a truncation
policy of zero bytes,

37
00:02:32,840 --> 00:02:34,140
so tool bodies are stripped.

38
00:02:34,800 --> 00:02:38,160
A tool result isn't evidence that a user
authorized anything,

39
00:02:38,520 --> 00:02:40,540
and the code comment says exactly that.

40
00:02:41,240 --> 00:02:44,200
Third, assistant messages stay untrusted.

41
00:02:45,000 --> 00:02:49,180
Guardian gets up to three recent user
messages, along with the assistant context

42
00:02:49,200 --> 00:02:49,920
that preceded them.

43
00:02:50,520 --> 00:02:54,100
The injected host note says assistant
messages are untrusted context,

44
00:02:54,540 --> 00:02:58,940
not authorization or verified questions,
and that this is partial history,

45
00:02:59,240 --> 00:03:00,840
not a transfer of permission.

46
00:03:01,375 --> 00:03:03,215
And it respects history boundaries.

47
00:03:03,395 --> 00:03:07,615
The reconstruction honors earlier
compactions and rollbacks.

48
00:03:07,715 --> 00:03:13,295
The PR's tests cover legacy and paginated
history, and they verify that sender

49
00:03:13,348 --> 00:03:18,095
evidence reaches only Guardian and
excludes rolled back permissions.

50
00:03:18,228 --> 00:03:22,255
So if a user granted something and then
you rolled that turn back,

51
00:03:22,275 --> 00:03:24,335
the receiving host doesn't see the grant.

52
00:03:24,463 --> 00:03:30,815
That's the cross host version of the
guarantee from 0.153.0.

53
00:03:31,093 --> 00:03:34,053
Now the part that matters operationally:
timing.

54
00:03:34,653 --> 00:03:36,993
Storage reads are wrapped in a five second
timeout.

55
00:03:37,493 --> 00:03:41,233
Sender context is captured before
acquiring the active turn lock,

56
00:03:41,793 --> 00:03:43,973
and then bound to the turn that accepts
the delivery.

57
00:03:44,473 --> 00:03:47,593
So if the receiver is mid turn while the
storage read is pending,

58
00:03:48,193 --> 00:03:49,473
that turn can finish normally.

59
00:03:50,053 --> 00:03:51,893
The read can't deadlock admission.

60
00:03:52,433 --> 00:03:56,093
The PR includes a gated storage read test
to verify exactly that.

61
00:03:56,292 --> 00:03:57,252
So how do you use it?

62
00:03:57,345 --> 00:04:00,292
There's no new flag or environment
variable here.

63
00:04:00,479 --> 00:04:05,652
It's automatic during turn input
admission, when a delegated message arrives from a

64
00:04:05,743 --> 00:04:08,212
sender that isn't resident locally.

65
00:04:08,372 --> 00:04:11,172
What you can do is change how you build
things.

66
00:04:11,359 --> 00:04:17,012
If you were working around missing sender
context by pasting quoted delegation text

67
00:04:17,044 --> 00:04:19,692
into the task payload, you can stop.

68
00:04:19,782 --> 00:04:25,012
The code comments are clear that quoted
delegation text and ordinary tool results

69
00:04:25,081 --> 00:04:29,972
cannot establish sender provenance, so
that workaround never counted as evidence

70
00:04:30,029 --> 00:04:30,532
anyway.

71
00:04:30,712 --> 00:04:36,052
You also don't need to keep idle sender
agents loaded on receiver machines just to

72
00:04:36,122 --> 00:04:37,612
satisfy Guardian.

73
00:04:37,716 --> 00:04:42,452
What does matter is that the sender's
thread is persisted in a store the receiving

74
00:04:42,516 --> 00:04:44,292
host can reach with authorization.

75
00:04:44,452 --> 00:04:49,812
The store owns the authorization decision,
including cross worker tenant boundaries.

76
00:04:50,113 --> 00:04:51,893
Caveats, and there are three.

77
00:04:52,693 --> 00:04:55,153
One: that five second ceiling.

78
00:04:55,593 --> 00:05:00,433
If the store is slow or partitioned, the
read aborts, and Guardian sees the line

79
00:05:00,913 --> 00:05:03,773
Host: No sender user messages are
available.

80
00:05:04,553 --> 00:05:08,653
That can mean a manual approval prompt
where you expected a smooth handoff.

81
00:05:09,293 --> 00:05:12,253
The brief for this episode flags that as
the likely symptom,

82
00:05:12,453 --> 00:05:17,553
so if you see unexpected prompts on
delegated tasks, check storage latency first.

83
00:05:17,833 --> 00:05:19,673
Two: persisted lag.

84
00:05:19,801 --> 00:05:25,033
When context comes from storage, Guardian
gets an explicit label: Read from

85
00:05:25,097 --> 00:05:29,913
persisted sender history; newer
unpersisted messages may be missing.

86
00:05:30,233 --> 00:05:34,633
A message the sender typed a moment ago
might not be on disk yet,

87
00:05:34,686 --> 00:05:37,673
so Guardian can be working from a slightly
stale picture.

88
00:05:37,860 --> 00:05:42,553
Three: assistant text still can't
authorize its own commands.

89
00:05:42,729 --> 00:05:47,273
Only the user messages count, and only the
three most recent ones.

90
00:05:47,572 --> 00:05:50,132
Some smaller items from October 5 through
7.

91
00:05:50,692 --> 00:05:57,192
Number 51595 adds an optional
excludedThreadIds field to the thread/list

92
00:05:57,352 --> 00:06:00,392
JSON RPC method and the SDK bindings.

93
00:06:01,012 --> 00:06:05,132
If your client shows a saved manual thread
order, you load those pinned threads

94
00:06:05,172 --> 00:06:07,632
separately, then list the rest without
duplicates.

95
00:06:08,212 --> 00:06:12,572
The server applies exclusions before the
limit and refills pages until the limit is

96
00:06:12,612 --> 00:06:14,112
met or history runs out.

97
00:06:14,375 --> 00:06:15,735
Details to get right there.

98
00:06:15,895 --> 00:06:20,775
The cap is 100 entries, and duplicates
count toward it.

99
00:06:20,908 --> 00:06:26,935
Go over, or send an invalid ID, and you
get JSON RPC error minus

100
00:06:27,015 --> 00:06:30,655
32602 instead of silent truncation.

101
00:06:30,745 --> 00:06:34,135
And you must send the same exclusion list
on every page.

102
00:06:34,455 --> 00:06:38,935
Omitted, null, or empty lists behave
exactly as before.

103
00:06:39,278 --> 00:06:40,278
Two build side notes.

104
00:06:40,758 --> 00:06:44,478
The packaging script now exposes
create_parser, assemble_package,

105
00:06:44,618 --> 00:06:49,458
and archive_package as separate helpers,
and it supports DotSlash artifacts with

106
00:06:49,518 --> 00:06:52,838
format gz by decompressing straight to the
destination.

107
00:06:53,538 --> 00:06:58,678
And the Windows ARM64 voice builds dropped
the Bazel host JVM override that forced

108
00:06:58,738 --> 00:07:03,598
interpreter mode, while Bazel itself moved
to 9.2.0 with a refreshed lockfile.

109
00:07:03,958 --> 00:07:06,318
That's mostly for people building Codex
from source.

110
00:07:06,583 --> 00:07:07,543
That's the update.

111
00:07:07,703 --> 00:07:12,663
If you run delegated agents across hosts,
take a look at your thread store latency

112
00:07:12,803 --> 00:07:15,543
and make sure sender threads get
persisted.

113
00:07:15,669 --> 00:07:18,503
Thanks for listening, and we'll be back
tomorrow.

