Skip to content

Sessions (--continue/--resume) + init scaffold; fix show-config provenance & HITL resume warning (0.6.27) - #105

Merged
dkedar7 merged 1 commit into
mainfrom
feat/sessions-init-provenance-hitl-quiet-0.6.27
Jul 31, 2026
Merged

dkedar7 merged 1 commit into
mainfrom
feat/sessions-init-provenance-hitl-quiet-0.6.27

Conversation

@dkedar7

@dkedar7 dkedar7 commented Jul 31, 2026

Copy link
Copy Markdown
Owner

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 --check clean.

#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.
  • A plain run starts a new-but-persisted session, so it can be continued later.
  • --continue and --resume are mutually exclusive.

Where state lives: ~/.langstage/sessions/<workspace-hash>.sqlite — under the config home, honoring LANGSTAGE_CONFIG_HOME (so it fits the CLI's existing config model), keyed by the resolved workspace path. A dedicated LANGSTAGE_CLI_SESSIONS_DIR overrides 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_id now genuinely persists across runs; /status surfaces 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 raises NotImplementedError for the async checkpointer methods the graph invokes (verified). So the durable saver is the async-native AsyncSqliteSaver over 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 own asyncio.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 with guarantees a clean open/close even on Ctrl-C. Adds a langgraph-checkpoint-sqlite dependency.

Verified end-to-end: the issue's counter agent reports 1 → 3 → 5 across three -c invocations, and a fresh run resets to 1.

#104langstage-cli init scaffold (feature)

Bridges --demo → "run my own graph": writes a minimal, immediately-runnable my_agent.py (the README's keyless stdlib StateGraph example — needs only langgraph, a base dep) plus a langstage.toml wired to it, so the next langstage-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-config TOML source-file attribution (fix)

When a global ~/.langstage/config.toml and a project langstage.toml were 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_langgraph resume WARNING (fix)

Answering a plain-string interrupt(...) with free text made ag_ui_langgraph log failed 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 surgical logging.Filter now drops only that one record on the emitting ag_ui_langgraph logger 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_workspace publishes LANGSTAGE_WORKSPACE_ROOT into os.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

…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant