You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is the project-tracking / roadmap issue for agent session intelligence
and viewport. It sequences work that #408 (session object: id, cwd, sandbox,
Redis, Blob) deliberately did not cover: what the model actually sees,
how the canvas pages history, and how first paint stays cheap.
#408 is the durable addressable session. This issue is the durable agent mind + scroll window on top of that object.
Estimates and phase boundaries are directional. Each phase must be planned as
its own parent create-plan issue before implementation.
Stop treating the session as an app payload (400 rows, 3.5M char slice,
8 MiB Blob, 8 MiB localStorage, paint everything). Treat it as:
Agent intelligence — the model should remember what it did and what it learned across turns, up to the model’s real context window (hundreds
of thousands to ~1M tokens on current catalog models). Asking “what did you
already find?” should work.
A viewport, not a dump — the canvas scrolls a window. Almost never
the whole 8 MiB. First paint must not JSON.stringify megabytes onto localStorage (Stop freeze).
One Blob still OK — keep a single transcript object if we can; spike
turn-seam byte offsets (or equivalent) so we can page without sharding
into thousands of part files.
The operator should not need to scroll at all if the agent still knows the work.
The lobotomy (2026-08 diagnosis)
Production durable turns no longer match this table for inference: A1
shipped a structured model-messages projection (meta.modelMessagesPointer);
A1-adj shipped a volatile per-turn freshness reminder (meta.freshnessReminderPointer);
A2 shipped session-owned meta.workingNotes; A3 shipped a window-derived
token fold budget; A6 shipped catalog-only skill inject. Remaining first-turn /
warehouse pain is A4 (no compaction) and A5 (viewport / slim local).
The rows below are the 2026-08 baseline.
Layer
What we do today
Why it feels broken
Persist
Blob / local snapshot up to 8 MiB, including dense tool_run cards
Stop persist + wasm_refresh hitch; first paint overpays
Inference
Single { prompt } string via formatPromptWithHistory — last 400 user/assistant/system/error rows, then 3.5M character tail. Notool_use/tool_result messages
Capped like an HTTP body; next turn cannot see tools
Tool evidence
tool_run is display-only — never folded
Agent re-reads files every turn; no session memory of tool work
Thinking
Ephemeral, not in SessionStore
Fine. But nothing else carries findings → each turn is a new assistant
Canvas
Hydrate up to 2048 ring rows; Load earlier 512
Paints work that is rarely revisited
Cloud
One JSON { messages: [...] } Blob
Cannot Range into “latest page” of a JSON array
Symptom: the assistant refers to “the previous assistant,” forgets research,
and the tab freezes on Stop — orthogonal problems, same root: we store a
warehouse and ship crumbs to the Gateway.
See Identity dissociation (2026-08-18) below for the four fault lines
and the streamTextresponse.messages lock on A1.
Target (directional — lock in phase plans)
Session (already shipped, #408) = id, tenant, cwd, sandbox, pointer
AgentMemory (this umbrella) = working notes + folded tool evidence
+ compacted history sized to model window
Viewport (this umbrella) = slim local tail + Blob pages/offsets
+ scrollbar that feels infinite
⬜ OPEN — blocked by this umbrella (moved off #408 / project 2). A1+A3 shipped; still needs A4 so a snapshot is honest compacted memory, not a raw window dump
⬜ OPEN — not the first-turn fix; blocked by A4 (A1+A3 shipped)
Baseline (do not re-litigate): #408 core session object, envelope + Blob
carrier (#515), tool_run display-only (#345), thinking collapse, status bar #328 / #538. File mutate freshness (#277RunFileFreshness) is shipped and stays per-POST — do not put fingerprints on the session (#693).
Sequence / phases
Intelligence first (forgetting is the product bug). Viewport can spike in
parallel once slim-local is specified; do not block memory work on pagination.
A3 shipped separately from A4 (budget is the policy; compaction is the
mechanism). A4 is next.
A1 lock (2026-08-15):#549 is native / structured tool_result on the
Gateway payload, not more prose in formatPromptWithHistory.
A1-adj lock (2026-08-19):#693 is not a fifth identity issue. A1 has
shipped, so the model does see last turn’s read_file and can think it has
an edit grant; #277’s ledger is empty every Function POST. Reminder plan is #941 (source #693). Shipped 2026-09-05:PR #943
(merge b0a81d6). Advisory volatile reminder; #277 stays the fail-closed gate.
A3 lock (2026-09-05):#551 is a window-derived token budget, not a tokenizer
and not occupancy. Durable seed trim at the /api/turns route boundary
(trimModelMessagesToBudget); host legacy fold (formatPromptWithHistory) is
token-budget first. Reserve is Pi-style max(16384, 15% × window); tokens are ceil(chars/4). Plan #944 / PR #945 (merge 9a049c6).
Occupancy meter stays #556; compaction stays #552.
Peer harness map (OpenCode / Pi / Oh My Pi / Codex / DeepSeek)
Steal payload ideas. Skip their disk/process model (JSONL, SQLite, part/*.json, Hashline, LSP-on-write, subagent worktrees, mailboxes,
homedir ~/.codex/memories).
Identity dissociation — operator report + research (2026-08-18)
Operator: agents do not recognize the previous session as themselves.
Each new send feels like the model’s first turn. This is not a foundation-model
defect.
It is the same “previous assistant / new hire each send” symptom in the
lobotomy table. A 2026-08-18 review of Pi, Oh My Pi, OpenCode, Codex CLI,
DeepSeek, and Vercel AI SDK streamText maps it onto four harness fault
lines we already have children for. Do not open a fifth issue.
A1 (#549) has since shipped (PRs #937 / #939); A1-adj (#693 / plan #941 / PR #943) shipped 2026-09-05.
A3 (#551 / plan #944 / PR #945)
shipped 2026-09-05. Remaining first-turn / warehouse pain is A4 (compaction) and A5 (viewport).
Fault line
What we do today
Child
1. Intermediate-step pruning
Shipped (A1). Durable turns persist truncated tool_use/tool_result via meta.modelMessagesPointer. 2026-08 baseline: formatPromptWithHistory folded only user/assistant/system/error into one { prompt } string; tool_run was display-only (#345).
Paint thinking stays ephemeral (correct). Model-facing replay is an A1 lock (per-provider). 2026-08 baseline dropped reasoning parts and Anthropic/DeepSeek signatures.
#549 A1 ✅ (model-facing only). SessionStore CoT stays out (non-goal).
3. Role inversion
Shipped (A1). Structured toolCallId pairing on the wire. 2026-08 baseline: legacy system tool lines became Tool: … prose with no pairing.
Shipped (A3). Fold budget is catalog context_window − Pi-style reserve (max(16384, 15% × window)), tokens ceil(chars/4). 400 is a row-count safety rail; 3.5M is a host transport backstop. Compaction (when the window is full) is still A4. 2026-08 baseline: dialogue.slice(-400) then out.slice(-3_500_000).
#550 A2 (working notes) is complementary, not the first-turn fix. A notes
block on top of a pruned transcript is still a new assistant reading someone
else’s diary. A1 shipped first (evidence to remember); A2 notes now carry
identity until A4 compaction exists to write into.
lib/agent/runAgent.ts uses streamText + stepCountIs. The SDK’s onFinish / result.response.messages is the completeModelMessage[]
for the turn: assistant tool-calls, role: 'tool' results, reasoning parts,
final text — not the terminal assistant string the 2026-08 fold flattened.
A1 persists that array (bounded / truncated) as the model-facing history
and sends it as a messages array on turn N+1. Do not add more Tool:
lines to formatPromptWithHistory. Paint stays tool_run cards
(feature-divide). Abort/cancel: persist committed tool pairs the same way we
persist change_dir.
Peer split-result (Pi): LLM payload ≠ UI payload. We already have that split
in spirit (tool_run paint vs what A1 must send). Do not stuff paint JSON
into the Gateway.
Reasoning lock (do not silently flip)
Paint / SessionStore: thinking rows stay ephemeral. Do not dump CoT
into Redis/Blob as the memory mechanism.
Model-facing replay: A1 locked, per provider, whether reasoning
parts + providerMetadata ride the structured messages array (required
for DeepSeek-R1 / Anthropic thinking continuation) or stay dropped. That
remains an A1 lock, not a new issue.
Compaction (A4) — tool-pair atomicity
When we compact, an assistant tool-call and its tool-result are kept or
summarized together. Never slice(-N) across that boundary. Checkpoint
is labeled summary, never live assistant prose (#552 already).
Four investigations of non-cloud harnesses (Codex, DeepSeek Harness, OpenCode,
Oh My Pi). Comment on this issue has the ranked steal/skip list. Locks that
belong on children, not a new identity bug:
Persist the compact replacement. Codex Compacted.replacement_history /
OpenCode typed compaction message so resume == live post-compact view
(compaction: model-capability context compaction (not API payload slices) #552). We have no FTS over shadowed history — files-touched + notes must
carry the investigation.
Diff the world. Codex world-state / OpenCode Context Epoch: freeze block 1;
chronological system diffs; rewrite the prefix only at compact (inference: cache-stable two-block system prompt (don’t bust the KV prefix) #558).
OMP sticky RULES.md: re-attach standing orders near the leaf so A4 cannot
bury the persona snapshot.
The agent-session label is the auto-add trigger for this board. Do not also put #548–#558 / #560 / #372 / #693 / #694 on the #408 session-architecture board (project 2). That board is the shipped session object track. This umbrella is a different product problem (model memory + viewport).
Title intent
This is the project-tracking / roadmap issue for agent session intelligence
and viewport. It sequences work that #408 (session object: id, cwd, sandbox,
Redis, Blob) deliberately did not cover: what the model actually sees,
how the canvas pages history, and how first paint stays cheap.
#408 is the durable addressable session. This issue is the durable
agent mind + scroll window on top of that object.
Estimates and phase boundaries are directional. Each phase must be planned as
its own parent
create-planissue before implementation.Goal
Stop treating the session as an app payload (400 rows, 3.5M char slice,
8 MiB Blob, 8 MiB
localStorage, paint everything). Treat it as:learned across turns, up to the model’s real context window (hundreds
of thousands to ~1M tokens on current catalog models). Asking “what did you
already find?” should work.
the whole 8 MiB. First paint must not
JSON.stringifymegabytes ontolocalStorage(Stop freeze).turn-seam byte offsets (or equivalent) so we can page without sharding
into thousands of part files.
The operator should not need to scroll at all if the agent still knows the work.
The lobotomy (2026-08 diagnosis)
Production durable turns no longer match this table for inference: A1
shipped a structured model-messages projection (
meta.modelMessagesPointer);A1-adj shipped a volatile per-turn freshness reminder (
meta.freshnessReminderPointer);A2 shipped session-owned
meta.workingNotes; A3 shipped a window-derivedtoken fold budget; A6 shipped catalog-only skill inject. Remaining first-turn /
warehouse pain is A4 (no compaction) and A5 (viewport / slim local).
The rows below are the 2026-08 baseline.
tool_runcardswasm_refreshhitch; first paint overpays{ prompt }string viaformatPromptWithHistory— last 400 user/assistant/system/error rows, then 3.5M character tail. Notool_use/tool_resultmessagestool_runis display-only — never folded{ messages: [...] }BlobSymptom: the assistant refers to “the previous assistant,” forgets research,
and the tab freezes on Stop — orthogonal problems, same root: we store a
warehouse and ship crumbs to the Gateway.
See Identity dissociation (2026-08-18) below for the four fault lines
and the
streamTextresponse.messageslock on A1.Target (directional — lock in phase plans)
Key invariant: inference budget ≠ storage budget ≠ paint budget.
Source issues
tool_resulton the wire (notTool:crumbs)Baseline (do not re-litigate): #408 core session object, envelope + Blob
carrier (#515),
tool_rundisplay-only (#345), thinking collapse, status bar#328 / #538. File mutate freshness (#277
RunFileFreshness) is shipped andstays per-POST — do not put fingerprints on the session (#693).
Sequence / phases
Intelligence first (forgetting is the product bug). Viewport can spike in
parallel once slim-local is specified; do not block memory work on pagination.
tool_use/tool_result)A3 shipped separately from A4 (budget is the policy; compaction is the
mechanism). A4 is next.
A1 lock (2026-08-15): #549 is native / structured
tool_resulton theGateway payload, not more prose in
formatPromptWithHistory.A1-adj lock (2026-08-19): #693 is not a fifth identity issue. A1 has
shipped, so the model does see last turn’s
read_fileand can think it hasan edit grant; #277’s ledger is empty every Function POST. Reminder plan is
#941 (source #693).
Shipped 2026-09-05: PR #943
(merge
b0a81d6). Advisory volatile reminder; #277 stays the fail-closed gate.A3 lock (2026-09-05): #551 is a window-derived token budget, not a tokenizer
and not occupancy. Durable seed trim at the
/api/turnsroute boundary(
trimModelMessagesToBudget); host legacy fold (formatPromptWithHistory) istoken-budget first. Reserve is Pi-style
max(16384, 15% × window); tokens areceil(chars/4). Plan #944 /PR #945 (merge
9a049c6).Occupancy meter stays #556; compaction stays #552.
Peer harness map (OpenCode / Pi / Oh My Pi / Codex / DeepSeek)
Steal payload ideas. Skip their disk/process model (JSONL, SQLite,
part/*.json, Hashline, LSP-on-write, subagent worktrees, mailboxes,homedir
~/.codex/memories).tool_use/tool_resultartifact://); fail if retain fails (OpenCode)response.messages, stop flattening paintreplacement_history; OpenCode typedcompactionmessage)CustomMessageEntry/ durable noteswindow − reserve(~16k)retainedTail+ files-touched in the summarysystemdiffs; rewrite prefix only at compact)streamTextmessages, not Responses items)reasoning_content+tool_callsverbatim or attention collapsesstreamTextresponse.messages= full turn graph (not terminal assistant string)Identity dissociation — operator report + research (2026-08-18)
Operator: agents do not recognize the previous session as themselves.
Each new send feels like the model’s first turn. This is not a foundation-model
defect.
It is the same “previous assistant / new hire each send” symptom in the
lobotomy table. A 2026-08-18 review of Pi, Oh My Pi, OpenCode, Codex CLI,
DeepSeek, and Vercel AI SDK
streamTextmaps it onto four harness faultlines we already have children for. Do not open a fifth issue.
A1 (#549) has since shipped (PRs #937 / #939); A1-adj (#693 / plan #941 /
PR #943) shipped 2026-09-05.
A3 (#551 / plan #944 / PR #945)
shipped 2026-09-05. Remaining first-turn / warehouse pain is A4 (compaction) and A5 (viewport).
tool_use/tool_resultviameta.modelMessagesPointer. 2026-08 baseline:formatPromptWithHistoryfolded only user/assistant/system/error into one{ prompt }string;tool_runwas display-only (#345).providerMetadatastrippedtoolCallIdpairing on the wire. 2026-08 baseline: legacysystemtool lines becameTool: …prose with no pairing.context_window− Pi-style reserve (max(16384, 15% × window)), tokensceil(chars/4). 400 is a row-count safety rail; 3.5M is a host transport backstop. Compaction (when the window is full) is still A4. 2026-08 baseline:dialogue.slice(-400)thenout.slice(-3_500_000).#550 A2 (working notes) is complementary, not the first-turn fix. A notes
block on top of a pruned transcript is still a new assistant reading someone
else’s diary. A1 shipped first (evidence to remember); A2 notes now carry
identity until A4 compaction exists to write into.
A1 mechanism —
streamTextresponse.messages(shipped)lib/agent/runAgent.tsusesstreamText+stepCountIs. The SDK’sonFinish/result.response.messagesis the completeModelMessage[]for the turn: assistant tool-calls,
role: 'tool'results, reasoning parts,final text — not the terminal assistant string the 2026-08 fold flattened.
A1 persists that array (bounded / truncated) as the model-facing history
and sends it as a messages array on turn N+1. Do not add more
Tool:lines to
formatPromptWithHistory. Paint staystool_runcards(feature-divide). Abort/cancel: persist committed tool pairs the same way we
persist
change_dir.Peer split-result (Pi): LLM payload ≠ UI payload. We already have that split
in spirit (
tool_runpaint vs what A1 must send). Do not stuff paint JSONinto the Gateway.
Reasoning lock (do not silently flip)
into Redis/Blob as the memory mechanism.
reasoningparts +
providerMetadataride the structured messages array (requiredfor DeepSeek-R1 / Anthropic thinking continuation) or stay dropped. That
remains an A1 lock, not a new issue.
Compaction (A4) — tool-pair atomicity
When we compact, an assistant
tool-calland itstool-resultare kept orsummarized together. Never
slice(-N)across that boundary. Checkpointis labeled summary, never live assistant prose (#552 already).
What this research does not change
/responses/compactas ourruntime — steal the invariant, not the process
Orrery takeaways (2026-08-15)
Steal when and measure. Do not steal their runtime.
Skip: Go binary, SQLite SoT, hashline, first-party multi-provider router, localhost-no-auth, learned policy, replay eval.
Source-level peer notes (2026-08-19)
Four investigations of non-cloud harnesses (Codex, DeepSeek Harness, OpenCode,
Oh My Pi). Comment on this issue has the ranked steal/skip list. Locks that
belong on children, not a new identity bug:
Mixing them is how compaction wipes a
catwhile the handbook never had it.tool_resultis a snapshot. plan: agent read-before-edit + external file freshness (parent) #277 alreadyempties the observation ledger every Function POST. Prompt-side reminder is
agent memory: tool_result snapshots are not live file views (re-read or they’re stale) #693. Do not rewrite results; do not put fingerprints on
meta.llm.stream.We already reload (stateless Functions). A1 persists
response.messagesand stopped flattening
tool_runpaint (agent memory: structured truncated tool_result on the wire (not Tool: crumbs) #549).Compacted.replacement_history/OpenCode typed
compactionmessage so resume == live post-compact view(compaction: model-capability context compaction (not API payload slices) #552). We have no FTS over shadowed history — files-touched + notes must
carry the investigation.
chronological
systemdiffs; rewrite the prefix only at compact (inference: cache-stable two-block system prompt (don’t bust the KV prefix) #558).OMP sticky RULES.md: re-attach standing orders near the leaf so A4 cannot
bury the persona snapshot.
default off (OMP, not Codex-on-by-default). Cloud-shaped, not
~/.codex.later: cross-session handbook (idle extract, default off) — not the first-turn fix #694 Later.
Skip (unchanged): homedir JSONL, client SQLite, snapcompact PNGs, hashline as
the edit dialect, mnemopi embeddings as core, file-watcher inbox as v1.
Non-goals (this umbrella)
part/*.jsonfiles or client SQLite as SoTanthropic.txt/beast.txtpacks (optional later, not this board)MEMORY.mdwrites duringrunAgent(later: cross-session handbook (idle extract, default off) — not the first-turn fix #694 is later, off)Board
Only agent session architecture (user project 3).
The
agent-sessionlabel is the auto-add trigger for this board. Do not also put #548–#558 / #560 / #372 / #693 / #694 on the #408 session-architecture board (project 2). That board is the shipped session object track. This umbrella is a different product problem (model memory + viewport).Phase issues