Import a Claude Code session the machine already has
Claude Code keeps every session as JSONL under `~/.claude/projects/`, and the CLI continues one with `--resume <id>`. `claude.rs` already resumes whenever it finds a resume token in the session directory, for crash recovery -- so importing is that same path with the token written before the driver starts, and there is deliberately no second way to begin a session. The seed goes through `launch` with the ordinary spawn, so the driver never learns which kind it got. Two things the machine answers and the phone does not. **Which sessions exist.** One command per setup rather than one per file, for the reason discovery already gives: over ssh each would be its own connection. Titles come from the first few user records rather than the first, because a session opens with records the CLI injected -- slash commands, caveats around local command output -- which are stored as ordinary user records without the meta flag, so titling by "first user record" produced a list where most rows read `<command-name>/clear`. **Which file an id names.** The phone sends an id and never a path; the server looks it up again among the sessions it enumerated. An enrolled token must not be able to turn a spawn into "read me this file", which is the same rule that keeps a provider's command out of `POST /setups`. Only the tail is replayed. The imported conversation is for reading -- continuing it is the CLI's job, and it reads the whole file itself -- so this is a display budget, and it has to be one: the session this was written in is 39 MB, and all of it would otherwise cross a tunnel to a phone. A recorded working directory can outlive itself, which this found immediately: every session from before the checkouts moved to `~/repos` still records `~/host/repos/...`. Resuming into one fails at `cd` before the CLI starts -- a confusing way to meet a feature whose promise is "carry on where you left off" -- so the directory is checked, and a missing one is dropped with a log line naming it rather than being passed on to fail. Verified against this very session: 905 events replayed from the tail (351 tool calls, 350 results, 185 assistant messages, 19 mine), the resume token pointing at its id, and the stale directory reported and dropped. The list was read on the emulator, where the top row is that session under its opening sentence.
This commit is contained in:
1 parent
2a1bc84c1e
commit
6bbc829a3e
9 files changed
+726
-19
No files matched your search
@@ -26,6 +26,9 @@ private sealed class Screen {
|
||||
|
||||
data object Spawn : Screen()
|
||||
|
||||
/** Continuing a session the machine already had, rather than starting an empty one. */
|
||||
data object Import : Screen()
|
||||
|
||||
/**
|
||||
* Reached from a session rather than from the list, because usage belongs to the provider
|
||||
* running that session and not to the app. It carries the session back with it so Back returns
|
||||
@@ -102,6 +105,7 @@ fun AppRoot(settingsVersion: Int) {
|
||||
reloadToken = reloadToken,
|
||||
onOpen = { screen = Screen.Session(it) },
|
||||
onSpawn = { screen = Screen.Spawn },
|
||||
onImport = { screen = Screen.Import },
|
||||
onModels = { screen = Screen.Models },
|
||||
onSetups = { screen = Screen.Setups },
|
||||
onSettings = { screen = Screen.Settings },
|
||||
@@ -122,6 +126,15 @@ fun AppRoot(settingsVersion: Int) {
|
||||
},
|
||||
onBack = goToList,
|
||||
)
|
||||
is Screen.Import ->
|
||||
ImportScreen(
|
||||
settings = current,
|
||||
onImported = { imported ->
|
||||
reloadToken++
|
||||
screen = Screen.Session(imported)
|
||||
},
|
||||
onBack = goToList,
|
||||
)
|
||||
is Screen.Usage ->
|
||||
UsageScreen(
|
||||
settings = current,
|
||||
|
||||
Reference in new issue
Block a user