fix(coding-agent): persist user input source on user message entries
Summary
A persisted user message now records where its input came from. inputSource is interactive, rpc or extension, the same value the input's input event reported (source ?? "interactive").
Part of code-yeongyu/oh-my-openagent#10378 (item 2b). omo's plan gate (plan-consultant / plan-reviewer) keeps per-session state built from input events. After a restart or restore that state is gone, and the transcript cannot rebuild the user-request channel because it does not say whether a user entry was typed or injected by an extension. The omo side replays the transcript and trusts only entries that carry this field.
What changed
agent-session.ts: the user messageprompt()builds and the one_enqueuePreparedInputbuilds for queued steer/follow-up input carryinputSource. That object is the onemessage_endpersists, so the session entry keeps it, the same wayclientMessageIdalready does.sendUserMessage's fallback enqueue after a rejectedprompt()now passessource: "extension". Before, it defaulted to interactive.- Input restored from durable custody (
restoreQueuedInput) or requeued by a session move ran noinputevent and stays unstamped. Consumers must treat an absent value as unknown origin, never as typed input;docs/session-format.mdsays so. core/user-input-source.ts(new helper),core/changes.mdentry, CHANGELOG Added entry.
Evidence (head ed36d3d6d)
- RED:
test/suite/regressions/omo-10378-user-input-source.test.tsfailed 4/4 before the fix, each withinputSource: undefined(typed prompt, rpc prompt, idlesendUserMessage, and queued steer/typed follow-up/injected follow-up). - GREEN: that file plus
agent-session-queue,pending-input-sessionandextension-edit-user-message: 4 files, 86/86 passed. bun run check: exit 0.- Real CLI (senpi-qa harness,
--mode rpcwith session persistence, mock openai-completions server): two RPC prompts, and the session JSONL's user entries read back[{"text":"first rpc prompt","inputSource":"rpc"},{"text":"second rpc prompt","inputSource":"rpc"}]. The real auth file was unchanged (harness guard). Evidence held locally:
44e0f69d5581d53f5ce065731ca16341c2bc4bcd4829bfc1be56cd78a84467bc qa-driver.mjs
f4447001dda108049b2ac75dac226aaeecf6dc9a7334747940f343e730595f91 persisted-user-entries.json
- Full
packages/coding-agentsuite: running; result to follow in a comment.
Summary by cubic
Persisted user messages now record where their input came from so a resumed session's transcript can rebuild per-session trust. inputSource on the message session entry is interactive, rpc, or extension, matching the input event's source ?? "interactive". Part of code-yeongyu/oh-my-openagent#10378, where the plan gate must tell typed input apart from extension-injected input after a restart.
- The source lives on the session entry only and never reaches a provider request.
- Queued input restored after a host restart keeps its recorded source, including a prompt queued as a steer after losing the start race; input requeued by a session move, edited input, and pre-change records carry no
inputSourceand must be treated as unknown origin (documented indocs/session-format.md). sendUserMessage's fallback enqueue after a rejectedprompt()now passessource: "extension"instead of defaulting to interactive.