Sessions (--continue/--resume) + init scaffold; fix show-config provenance & HITL resume warning (0.6.27) - #105
Merged
Conversation
…nance & HITL resume warning (0.6.27) Ships four cli-local changes as one release (gh #101, #102, #103, #104). #102 (feature): first-class cross-invocation session persistence. The CLI now attaches a durable, per-workspace checkpointer itself (no user saver, no graph edits): --continue/-c resumes the most recent session (fresh + note if none), --resume <id> resumes a specific thread (exact id or unambiguous prefix; bare --resume / --list-sessions lists them), and a plain run starts a new-but-persisted session. --continue and --resume are mutually exclusive. State lives at ~/.langstage/sessions/<workspace-hash>.sqlite (honors LANGSTAGE_CONFIG_HOME; override with LANGSTAGE_CLI_SESSIONS_DIR) with a small JSON index. On by default (--no-persist / LANGSTAGE_PERSIST=0 / [session] persist=false to disable); a user-supplied checkpointer always wins. Because the AG-UI path is async-only and the sync SqliteSaver raises NotImplementedError for the async checkpointer methods, the durable saver is AsyncSqliteSaver over the same file, opened/closed within each turn's own event loop (aiosqlite can't cross loops). Adds langgraph-checkpoint-sqlite. #104 (feature): `langstage-cli init` scaffolds a runnable my_agent.py (the keyless stdlib StateGraph example) + a langstage.toml wired to it, so the next `langstage-cli "..."` runs the user's own graph with no -a. Refuses to overwrite without --force. #101 (fix): --show-config now attributes each merged TOML value to the file it actually came from (a global-only value is no longer mislabelled as the project file). cli-local: post-processes the resolved provenance without touching core. #103 (fix): a free-text interrupt() resume no longer leaks the benign ag_ui_langgraph "failed to parse resume_input as JSON" WARNING to the console — a surgical logging.Filter drops only that record on the emitting logger during the interactive resume, so no other warning is hidden. Tests: +13 regression tests (164 total), hermetic (temp session store + config home; also plugs apply_workspace's env-publishing leak between tests). ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011HWCfJii6gXd3XL3Gq3W8B
This was referenced Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ships four cli-local changes as one release (0.6.27): two features and two bug fixes. Full suite green (164 passed, +13 regression tests),
ruff check+ruff format --checkclean.#102 — first-class session persistence:
--continue/--resume(feature, the big one)Every invocation used to be amnesiac (an in-memory checkpointer thrown away at process exit). The CLI now attaches a durable, per-workspace checkpointer itself — no user-supplied saver, no graph edits:
--continue/-c— resume the most recent session for this workspace (starts fresh, with a note, if there is none).--resume <id>— resume a specific thread (exact id or unambiguous prefix). A bare--resume(or--list-sessions) lists resumable sessions.--continueand--resumeare mutually exclusive.Where state lives:
~/.langstage/sessions/<workspace-hash>.sqlite— under the config home, honoringLANGSTAGE_CONFIG_HOME(so it fits the CLI's existing config model), keyed by the resolved workspace path. A dedicatedLANGSTAGE_CLI_SESSIONS_DIRoverrides the whole location (also the test-hermeticity lever). A small JSON index (<hash>.index.json: thread id, created/updated timestamps, first-message snippet) drives--continue(most-recently-updated) and--list-sessions. Persistence is on by default (disable with--no-persist,LANGSTAGE_PERSIST=0, or[session] persist = false). A graph that compiles in its own checkpointer keeps it (yours always wins); a pinned[configurable] thread_idnow genuinely persists across runs;/statussurfaces persistence state + store path.Design note (a genuine fork, resolved and documented): the issue suggested langgraph's sync
SqliteSaver, but the CLI's only execution path is the async AG-UI adapter, and the sync saver raisesNotImplementedErrorfor the async checkpointer methods the graph invokes (verified). So the durable saver is the async-nativeAsyncSqliteSaverover the same SQLite file. Its aiosqlite connection is bound to the event loop it's opened in and can't cross loops, and the CLI runs each turn in its ownasyncio.run, so the saver is opened/closed within each turn's loop (the agent is rebuilt there to wrap the graph with the live checkpointer). Persistence is durable regardless — the SQLite file is the store — so reopening per turn reads back every prior turn's state;async withguarantees a clean open/close even on Ctrl-C. Adds alanggraph-checkpoint-sqlitedependency.Verified end-to-end: the issue's counter agent reports
1 → 3 → 5across three-cinvocations, and a fresh run resets to1.#104 —
langstage-cli initscaffold (feature)Bridges
--demo→ "run my own graph": writes a minimal, immediately-runnablemy_agent.py(the README's keyless stdlibStateGraphexample — needs onlylanggraph, a base dep) plus alangstage.tomlwired to it, so the nextlangstage-cli "..."runs the user's own graph with no-a. Refuses to overwrite existing files unless--force; prints exactly what it wrote and the next step.#101 —
--show-configTOML source-file attribution (fix)When a global
~/.langstage/config.tomland a projectlangstage.tomlwere both merged, the resolver stamped every TOML-sourced value with the last file read (the project file), so a value set only globally was mislabelled[toml (langstage.toml)]. The CLI now post-processes the resolved provenance, walking the same files in merge order (project wins) and relabelling each value with the highest-precedence file that actually defines its key. cli-local — corrects the source map the shared resolver produced without touching core.#103 — quiet the benign
ag_ui_langgraphresume WARNING (fix)Answering a plain-string
interrupt(...)with free text madeag_ui_langgraphlogfailed to parse resume_input as JSON, treating as string (...)at WARNING on every response — an error-looking line above the (correct) answer, in default interactive mode. A surgicallogging.Filternow drops only that one record on the emittingag_ui_langgraphlogger during the interactive resume (a filter on an ancestor logger isn't consulted on propagation — verified — so it targets the emitting child), so no other warning is hidden. cli-local — no global logging filter in core.Tests
+13 regression tests (one+ per issue), all hermetic (temp session store + temp config home). Also plugs a latent isolation leak:
apply_workspacepublishesLANGSTAGE_WORKSPACE_ROOTintoos.environ, which a turn-running test would leak into the next test — an autouse fixture now snapshots/restores it.🤖 Generated with Claude Code
https://claude.ai/code/session_011HWCfJii6gXd3XL3Gq3W8B