Moves the stateless parts of the guest-session subsystem into
internal/daemon/session:
- consts (BackendSSH, attach/transport kinds, StateRoot, LogTailLineDefault)
- StateSnapshot plus ParseState / InspectStateFromDir / ApplyStateSnapshot / StateChanged
- 10 on-guest path helpers (StateDir, StdoutLogPath, StdinPipePath, …)
- 3 bash script generators (Script, InspectScript, SignalScript)
- small utilities (ShellQuote, ExitCode, CloneStringMap, TailFileContent,
ProcessAlive + syscallKill test seam, FormatStepError)
- launch helpers (DefaultName, DefaultCWD, FailLaunch,
NormalizeRequiredCommands, CWDPreflightScript, CommandPreflightScript,
AttachInputCommand, AttachTailCommand, EnvLines)
Callers inside the daemon package import the new package under the
alias "sess" to avoid colliding with the local `session model.GuestSession`
variables threaded through the orchestrator code. guest_sessions.go
shrinks from 616 → 156 LOC; session_stream.go, session_attach.go,
session_lifecycle.go, workspace.go, and guest_sessions_test.go rewire to
the exported names.
The orchestrator methods (StartGuestSession, BeginGuestSessionAttach,
SendToGuestSession, GuestSessionLogs, refresh/inspect, sessionRegistry,
guestSessionController) stay on *Daemon. Full Manager-style extraction
would need prerequisite phases (operation protocol, workdisk helpers),
mirroring Phase 4a's trade-off.
All tests green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
guest.session.send — write to a pipe-mode session's stdin without
holding the exclusive attach. The daemon dials a fresh SSH connection,
uploads the payload to a temp file, and cats it into the session's
named FIFO. Linux atomicity for writes ≤ PIPE_BUF covers all pi RPC
JSONL lines. Attach exclusivity is unchanged.
vm.workspace.export — pull changes from guest back to host. Runs
`git add -A && git diff --cached HEAD --binary` inside the guest via a
new RunScriptOutput helper on guest.Client (stdout-only capture,
distinct from RunScript which merges stderr). Returns a binary-safe
patch and a list of changed files. CLI writes the patch to stdout for
`| git apply` or to a file via --output.
RunScriptOutput is implemented as a direct SSH session (same pattern as
runSession) rather than going through StartCommand/StreamSession to
avoid closing the underlying Client, which is required since
ExportVMWorkspace calls it twice on the same connection.
New files: internal/daemon/workspace_test.go
Guest session cwd and command preflight helpers were emitting literal
`\\n` separators, so the guest shell saw malformed one-line scripts and
could fail `preflight_cwd` even when `/root/repo` already existed.
Replace those builders with real newlines, and fix the nearby attach
helper commands that were making the same mistake.
Add a small daemon guest-SSH seam so workspace preparation and session
start can share a fake backend in tests, then cover the regression with
an end-to-end daemon test for `PrepareVMWorkspace` followed by
`StartGuestSession` on `/root/repo`.
Validation: `GOCACHE=/tmp/banger-gocache go test ./internal/daemon` and
`GOCACHE=/tmp/banger-gocache go test ./...`.