Commit 0bdeab3
authored
fix(tui): recall accepted slash commands locally (openai#17336)
# TL;DR
- Adds recognized slash commands to the TUI's local in-session recall
history.
- This is the MVP of the whole feature: it keeps slash-command recall
local only: nothing is written to persistent history, app-server
history, or core history storage.
- Treats slash commands like submitted text once they parse as a known
built-in command, regardless of whether command dispatch later succeeds.
# Problem
Slash commands are handled outside the normal message submission path,
so they could clear the composer without becoming part of the local
Up-arrow recall list. That made command-heavy workflows awkward: after
running `/diff`, `/rename Better title`, `/plan investigate this`, or
even a valid command that reports a usage error, users had to retype the
command instead of recalling and editing it like a normal prompt.
The goal of this PR is to make slash commands feel like submitted input
inside the current TUI session while keeping the change deliberately
local. This is not persistent history yet; it only affects the
composer's in-memory recall behavior.
# Mental model
The composer owns draft state and local recall. When slash input parses
as a recognized built-in command, the composer stages the submitted
command text before returning `InputResult::Command` or
`InputResult::CommandWithArgs`. `ChatWidget` then dispatches the command
and records the staged entry once dispatch returns to the input-result
path.
Command-name recognition is the only validation before local recall. A
valid slash command is recallable whether it succeeds, fails with a
usage error, no-ops, is unavailable while a task is running, or is
skipped by command-specific logic. An unrecognized slash command is
different: it is restored as a draft, surfaces the existing
unrecognized-command message, and is not added to recall.
Bare commands recalled from typed text use the trimmed submitted draft.
Commands selected from the popup record the canonical command text, such
as `/diff`, rather than the partial filter text the user typed. Inline
commands with arguments keep the original command invocation available
locally even when their arguments are later prepared through the normal
submission pipeline.
# Non-goals
Persisting slash commands across sessions is intentionally out of scope.
This change does not modify app-server history, core history storage,
protocol events, or message submission semantics.
This does not change command availability, command side effects, popup
filtering, command parsing, or the semantics of unsupported commands. It
only changes whether recognized slash-command invocations are available
through local Up-arrow recall after the user submits them.
# Tradeoffs
The main tradeoff is that recall is based on command recognition, not
command outcome. This intentionally favors a simpler user model: if the
TUI accepted the input as a slash command, the user can recall and edit
that input just like plain text. That means valid-but-unsuccessful
invocations such as usage errors are recallable, which is useful when
the next action is usually to edit and retry.
The previous accept/reject design required command dispatch to report a
boolean outcome, which made the dispatcher API noisier and forced every
branch to decide history behavior. This version keeps the dispatch APIs
as side-effect-only methods and localizes history recording to the
slash-command input path.
Inline command handling still avoids double-recording by preparing
inline arguments without using the normal message-submission history
path. The staged slash-command entry remains the single local recall
record for the command invocation.
# Architecture
`ChatComposer` stages a pending `HistoryEntry` when recognized
slash-command input is promoted into an input result. The pending entry
mirrors the existing local history payload shape so recall can restore
text elements, local images, remote images, mention bindings, and
pending paste state when those are present.
`BottomPane` exposes a narrow method for recording that staged command
entry because it owns the composer. `ChatWidget` records the staged
entry after dispatching a recognized command from the input-result
match. Valid commands rejected before they reach `ChatWidget`, such as
commands unavailable while a task is running, are staged and recorded in
the composer path that detects the rejection.
Slash-command dispatch itself now lives in
`chatwidget/slash_dispatch.rs` so the behavior is reviewable without
adding more weight to `chatwidget.rs`. The extraction is
behavior-preserving: the dispatch match arms stay intact, while the
input flow in `chatwidget.rs` remains the single place that connects
submitted slash-command input to dispatch.
# Observability
There is no new logging because this is a local UI recall behavior and
the result is directly visible through Up-arrow recall. The practical
debug path is to trace Enter through
`ChatComposer::try_dispatch_bare_slash_command`,
`ChatComposer::try_dispatch_slash_command_with_args`, or popup Enter/Tab
handling, then confirm the recognized command is staged before dispatch
and recorded exactly once afterward.
If a valid command unexpectedly does not appear in recall, check whether
the input path staged slash history before clearing the composer and
whether it used the `ChatWidget` slash-dispatch wrapper. If an
unrecognized command unexpectedly appears in recall, check the parser
branch that should restore the draft instead of staging history.
# Tests
Composer-level tests cover staging and recording for a bare typed slash
command, a popup-selected command, and an inline command with arguments.
Chat-widget tests cover valid commands being recallable after normal
dispatch, inline dispatch, usage errors, task-running unavailability,
no-op stub dispatch, and command-specific skip behavior such as `/init`
when an instructions file already exists. They also cover the negative
case: unrecognized slash commands are not added to local recall.1 parent be13f03 commit 0bdeab3
5 files changed
Lines changed: 817 additions & 466 deletions
File tree
- codex-rs/tui/src
- bottom_pane
- chatwidget
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
32 | 36 | | |
33 | 37 | | |
34 | 38 | | |
| |||
228 | 232 | | |
229 | 233 | | |
230 | 234 | | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
231 | 239 | | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
232 | 245 | | |
233 | 246 | | |
234 | 247 | | |
| |||
311 | 324 | | |
312 | 325 | | |
313 | 326 | | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
314 | 332 | | |
315 | 333 | | |
316 | 334 | | |
| |||
434 | 452 | | |
435 | 453 | | |
436 | 454 | | |
| 455 | + | |
437 | 456 | | |
438 | 457 | | |
439 | 458 | | |
| |||
1063 | 1082 | | |
1064 | 1083 | | |
1065 | 1084 | | |
| 1085 | + | |
| 1086 | + | |
| 1087 | + | |
| 1088 | + | |
| 1089 | + | |
| 1090 | + | |
| 1091 | + | |
| 1092 | + | |
| 1093 | + | |
| 1094 | + | |
1066 | 1095 | | |
1067 | 1096 | | |
1068 | 1097 | | |
| |||
1281 | 1310 | | |
1282 | 1311 | | |
1283 | 1312 | | |
| 1313 | + | |
1284 | 1314 | | |
1285 | 1315 | | |
1286 | 1316 | | |
| |||
1305 | 1335 | | |
1306 | 1336 | | |
1307 | 1337 | | |
| 1338 | + | |
1308 | 1339 | | |
1309 | 1340 | | |
1310 | 1341 | | |
| |||
2272 | 2303 | | |
2273 | 2304 | | |
2274 | 2305 | | |
| 2306 | + | |
| 2307 | + | |
2275 | 2308 | | |
2276 | 2309 | | |
| 2310 | + | |
2277 | 2311 | | |
2278 | 2312 | | |
2279 | 2313 | | |
| |||
2303 | 2337 | | |
2304 | 2338 | | |
2305 | 2339 | | |
| 2340 | + | |
| 2341 | + | |
2306 | 2342 | | |
2307 | 2343 | | |
2308 | 2344 | | |
| 2345 | + | |
| 2346 | + | |
2309 | 2347 | | |
2310 | 2348 | | |
2311 | 2349 | | |
| |||
2320 | 2358 | | |
2321 | 2359 | | |
2322 | 2360 | | |
2323 | | - | |
| 2361 | + | |
2324 | 2362 | | |
2325 | 2363 | | |
| 2364 | + | |
| 2365 | + | |
| 2366 | + | |
| 2367 | + | |
2326 | 2368 | | |
2327 | 2369 | | |
2328 | 2370 | | |
| |||
2353 | 2395 | | |
2354 | 2396 | | |
2355 | 2397 | | |
| 2398 | + | |
| 2399 | + | |
| 2400 | + | |
| 2401 | + | |
| 2402 | + | |
| 2403 | + | |
| 2404 | + | |
| 2405 | + | |
| 2406 | + | |
| 2407 | + | |
| 2408 | + | |
| 2409 | + | |
| 2410 | + | |
| 2411 | + | |
| 2412 | + | |
| 2413 | + | |
| 2414 | + | |
| 2415 | + | |
| 2416 | + | |
| 2417 | + | |
| 2418 | + | |
| 2419 | + | |
| 2420 | + | |
| 2421 | + | |
| 2422 | + | |
| 2423 | + | |
| 2424 | + | |
| 2425 | + | |
| 2426 | + | |
| 2427 | + | |
| 2428 | + | |
| 2429 | + | |
| 2430 | + | |
| 2431 | + | |
| 2432 | + | |
| 2433 | + | |
| 2434 | + | |
2356 | 2435 | | |
2357 | 2436 | | |
2358 | 2437 | | |
| |||
7863 | 7942 | | |
7864 | 7943 | | |
7865 | 7944 | | |
| 7945 | + | |
| 7946 | + | |
| 7947 | + | |
| 7948 | + | |
| 7949 | + | |
| 7950 | + | |
| 7951 | + | |
| 7952 | + | |
| 7953 | + | |
| 7954 | + | |
| 7955 | + | |
| 7956 | + | |
| 7957 | + | |
| 7958 | + | |
| 7959 | + | |
| 7960 | + | |
| 7961 | + | |
| 7962 | + | |
| 7963 | + | |
| 7964 | + | |
| 7965 | + | |
| 7966 | + | |
| 7967 | + | |
| 7968 | + | |
| 7969 | + | |
| 7970 | + | |
| 7971 | + | |
| 7972 | + | |
| 7973 | + | |
| 7974 | + | |
| 7975 | + | |
| 7976 | + | |
| 7977 | + | |
| 7978 | + | |
| 7979 | + | |
| 7980 | + | |
| 7981 | + | |
| 7982 | + | |
| 7983 | + | |
| 7984 | + | |
| 7985 | + | |
| 7986 | + | |
| 7987 | + | |
| 7988 | + | |
| 7989 | + | |
| 7990 | + | |
| 7991 | + | |
| 7992 | + | |
| 7993 | + | |
| 7994 | + | |
| 7995 | + | |
| 7996 | + | |
| 7997 | + | |
| 7998 | + | |
| 7999 | + | |
| 8000 | + | |
| 8001 | + | |
| 8002 | + | |
| 8003 | + | |
| 8004 | + | |
| 8005 | + | |
| 8006 | + | |
| 8007 | + | |
| 8008 | + | |
| 8009 | + | |
| 8010 | + | |
| 8011 | + | |
| 8012 | + | |
| 8013 | + | |
| 8014 | + | |
| 8015 | + | |
| 8016 | + | |
| 8017 | + | |
| 8018 | + | |
| 8019 | + | |
| 8020 | + | |
| 8021 | + | |
| 8022 | + | |
| 8023 | + | |
| 8024 | + | |
| 8025 | + | |
| 8026 | + | |
| 8027 | + | |
| 8028 | + | |
7866 | 8029 | | |
7867 | 8030 | | |
7868 | 8031 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
280 | 280 | | |
281 | 281 | | |
282 | 282 | | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
283 | 291 | | |
284 | 292 | | |
285 | 293 | | |
| |||
0 commit comments