Name local agent sessions after the issue branch and resume them #17

Open
Alfred wants to merge 1 commit from agent/issue-7-add-a-name-on-ai-client-and-resume-from into main
Owner

What

Every local-agent run of an issue mailbox (analysis, development, ci_fix, review_fix, conflict_resolution, answer) names its CLI session after the issue branch and resumes the session the previous run of the same adapter left (spec §16.5).

How

  • Contracts: SessionRef, the ResumeFallback enum, LlmRequest::$session, LlmResult::$sessionId/$resumed/$resumeFallback, and LlmCapabilities::$resumableSessions/$sessionStatePath (the path must be inside /home/agent).
  • Setting: AGENT_SESSION_RESUME, default true, organization and project scope.
  • Detection: the capability is lowered to alfred.agents.{id}.resume_versions and is never granted on the host runner. It is stored in agent_installations.resumable_sessions and shown on the agents screen.
  • Adapters:
    • Claude Code: --session-id <random uuid> for a fresh session, --resume <id> to resume, --name <branch> on verified versions. "No conversation found" leads to a fallback with state_missing.
    • Codex: exec resume <id>, with the id taken from thread.started.
    • OpenCode, Copilot CLI and Grok Build stay without resume (state stored next to credentials or settings, or not verified).
  • Silent fallback: a resume failure the adapter recognises re-runs the CLI once in a fresh session, within the remaining timeout. No retry, status, label or comment change.
  • Engine:
    • SessionResolver applies resume only for an issue mailbox, a container runner, a local adapter, an installed version that resumes, the setting on, and no credential mount overlapping the session directory.
    • The prior id is read from llm_sessions by mailbox and adapter only. Provider and model are not part of the key.
  • State: AgentSessionStore keeps it in {base}/.agent-sessions/{org}-{project}-issue-{iid}/{adapter} (mode 0700). It is mounted read-write at sessionStatePath, under a tmpfs owned by the worker uid:gid, and removed by MailboxCleanup.
  • Branch name: branch_name is frozen at the first run, whatever the phase. A read-only run uses the branch only once it exists on the remote, otherwise a temporary branch, so analysis creates no branch.
  • Data: llm_sessions gains session_name, native_session_id, resumed and resume_fallback, recorded whatever the outcome.
  • Run screen: a Session row shows the name, the native id and "resumed" or "fresh (reason)".

Tests

  • Unit: adapter argv (no session, fresh, resume), label gating, native id parsing, fallback with the remaining timeout, timeout when no time is left, no fallback on an ambiguous failure, host runs, detection ranges, LlmCapabilities validation, the sandbox mount and tmpfs.
  • Feature: two consecutive runs resume; first run no_prior_session; adapter_changed; a model change still resumes; refused resume falls back silently; a failed run's session is recorded; every off case (setting off at either scope, version or adapter without the capability, MR mailbox, API mode, host runner); the analysis run freezes the name without creating the branch; cleanup removes the state; detector storage; run screen rendering.
  • Browser (Pest + Playwright): the run screen session fields. Not run locally (no Node on the dev host); all three CI pipelines already run php artisan test with Playwright.

Verification

  • The Claude Code flags and its missing-session message were checked against Claude Code 2.1.311.
  • The mount layout (worker-owned parent tmpfs plus the read-write session bind) was checked with Docker 29.9.2: the CLI can write next to its sessions, and session files persist on the host.

To review

  • The Codex resume flags and version range (>=0.46 <1.0) are not verified, because Codex isn't installed on the dev host.
  • A credential mount overlapping the session directory (such as /srv/claude:/home/agent/.claude) turns resume off for that run, with a logged warning.

🤖 Generated with Claude Code

Closes #7

## What Every local-agent run of an issue mailbox (analysis, development, `ci_fix`, `review_fix`, `conflict_resolution`, `answer`) names its CLI session after the issue branch and resumes the session the previous run of the same adapter left (spec §16.5). ## How - **Contracts:** `SessionRef`, the `ResumeFallback` enum, `LlmRequest::$session`, `LlmResult::$sessionId/$resumed/$resumeFallback`, and `LlmCapabilities::$resumableSessions/$sessionStatePath` (the path must be inside `/home/agent`). - **Setting:** `AGENT_SESSION_RESUME`, default `true`, organization and project scope. - **Detection:** the capability is lowered to `alfred.agents.{id}.resume_versions` and is never granted on the host runner. It is stored in `agent_installations.resumable_sessions` and shown on the agents screen. - **Adapters:** - Claude Code: `--session-id <random uuid>` for a fresh session, `--resume <id>` to resume, `--name <branch>` on verified versions. "No conversation found" leads to a fallback with `state_missing`. - Codex: `exec resume <id>`, with the id taken from `thread.started`. - OpenCode, Copilot CLI and Grok Build stay without resume (state stored next to credentials or settings, or not verified). - **Silent fallback:** a resume failure the adapter recognises re-runs the CLI once in a fresh session, within the remaining timeout. No retry, status, label or comment change. - **Engine:** - `SessionResolver` applies resume only for an issue mailbox, a container runner, a local adapter, an installed version that resumes, the setting on, and no credential mount overlapping the session directory. - The prior id is read from `llm_sessions` by mailbox and adapter only. Provider and model are not part of the key. - **State:** `AgentSessionStore` keeps it in `{base}/.agent-sessions/{org}-{project}-issue-{iid}/{adapter}` (mode 0700). It is mounted read-write at `sessionStatePath`, under a tmpfs owned by the worker uid:gid, and removed by `MailboxCleanup`. - **Branch name:** `branch_name` is frozen at the first run, whatever the phase. A read-only run uses the branch only once it exists on the remote, otherwise a temporary branch, so analysis creates no branch. - **Data:** `llm_sessions` gains `session_name`, `native_session_id`, `resumed` and `resume_fallback`, recorded whatever the outcome. - **Run screen:** a Session row shows the name, the native id and "resumed" or "fresh (reason)". ## Tests - **Unit:** adapter argv (no session, fresh, resume), label gating, native id parsing, fallback with the remaining timeout, timeout when no time is left, no fallback on an ambiguous failure, host runs, detection ranges, `LlmCapabilities` validation, the sandbox mount and tmpfs. - **Feature:** two consecutive runs resume; first run `no_prior_session`; `adapter_changed`; a model change still resumes; refused resume falls back silently; a failed run's session is recorded; every off case (setting off at either scope, version or adapter without the capability, MR mailbox, API mode, host runner); the analysis run freezes the name without creating the branch; cleanup removes the state; detector storage; run screen rendering. - **Browser (Pest + Playwright):** the run screen session fields. Not run locally (no Node on the dev host); all three CI pipelines already run `php artisan test` with Playwright. ## Verification - The Claude Code flags and its missing-session message were checked against Claude Code 2.1.311. - The mount layout (worker-owned parent tmpfs plus the read-write session bind) was checked with Docker 29.9.2: the CLI can write next to its sessions, and session files persist on the host. ## To review - The Codex resume flags and version range (`>=0.46 <1.0`) are not verified, because Codex isn't installed on the dev host. - A credential mount overlapping the session directory (such as `/srv/claude:/home/agent/.claude`) turns resume off for that run, with a logged warning. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- agent:key=ce450b556c61:kind=merge_request --> Closes #7
feat: name local agent sessions after the issue branch and resume them
All checks were successful
CI / checks (pull_request) Successful in 3m8s
CI / checks (push) Successful in 4m6s
5d2f5d1506
Every local-agent run of an issue mailbox now names its CLI session after
the issue branch and resumes the native session the previous run of the
same adapter left (spec §16.5).

- Contracts: SessionRef, ResumeFallback, LlmRequest::$session,
  LlmResult::$sessionId/$resumed/$resumeFallback, and the capabilities
  resumableSessions/sessionStatePath (a path inside the home).
- AGENT_SESSION_RESUME setting (default true, organization and project).
- Detection lowers resumableSessions to alfred.agents.{id}.resume_versions
  and never grants it on the host runner; agent_installations stores it.
- LocalAgentAdapter: session modes (fresh or resume), the native id read
  from the output, and one silent fresh fallback, within the remaining
  timeout, when a resume fails in a way the adapter recognises.
- Claude Code: --session-id <random uuid>, --resume <id>, --name on
  verified versions, "No conversation found" maps to state_missing.
  Codex: exec resume <id>, thread id from thread.started. OpenCode,
  Copilot CLI and Grok Build stay without resume.
- Engine: SessionResolver (issue mailbox, container runner, local
  adapter, installed version, setting, no credential mount overlap), with
  the prior id read from llm_sessions by mailbox and adapter only.
  AgentSessionStore keeps state in
  {base}/.agent-sessions/{org}-{project}-issue-{iid}/{adapter}, mode 0700,
  mounted read-write at sessionStatePath under a worker-owned tmpfs.
- branch_name is frozen at the first run whatever the phase; read-only
  runs use the branch only once it exists on the remote.
- llm_sessions gains session_name, native_session_id, resumed and
  resume_fallback, recorded whatever the outcome.
- MailboxCleanup removes the mailbox's session state.
- The run screen shows the session name, native id and resumed or fresh
  reason; the agents screen shows which versions resume sessions.
- FakeLlmAdapter gains withResumableSessions() and refuseNextResume().

Refs: #7
Alfred self-assigned this 2026-11-04 10:04:56 +00:00
Alfred changed title from WIP: Name local agent sessions after the issue branch and resume them to Name local agent sessions after the issue branch and resume them 2026-11-04 10:17:07 +00:00
Alfred removed their assignment 2026-11-04 10:17:08 +00:00
All checks were successful
CI / checks (pull_request) Successful in 3m8s
CI / checks (push) Successful in 4m6s
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin agent/issue-7-add-a-name-on-ai-client-and-resume-from:agent/issue-7-add-a-name-on-ai-client-and-resume-from
git switch agent/issue-7-add-a-name-on-ai-client-and-resume-from

Merge

Merge the changes and update on Forgejo.
git switch main
git merge --no-ff agent/issue-7-add-a-name-on-ai-client-and-resume-from
git switch agent/issue-7-add-a-name-on-ai-client-and-resume-from
git rebase main
git switch main
git merge --ff-only agent/issue-7-add-a-name-on-ai-client-and-resume-from
git switch agent/issue-7-add-a-name-on-ai-client-and-resume-from
git rebase main
git switch main
git merge --no-ff agent/issue-7-add-a-name-on-ai-client-and-resume-from
git switch main
git merge --squash agent/issue-7-add-a-name-on-ai-client-and-resume-from
git switch main
git merge --ff-only agent/issue-7-add-a-name-on-ai-client-and-resume-from
git switch main
git merge agent/issue-7-add-a-name-on-ai-client-and-resume-from
git push origin main
Sign in to join this conversation.
No description provided.