Repository navigation
feat(fork): visible and controllable agent sessions (#6, #7) - #15
Conversation
Add the approved design for session visibility, resume, cold start and user control (#6), plus the short design for refreshing idle clocks on turn completion (#7, port of block#7173). Register both specs in the fork patch inventory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Design notes for this work are kept outside the repository from now on. Drop the two specs and restore the patch inventory to the base. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Add result_refreshes_idle_clock, an exhaustive mapping from PromptSource to whether a completed result refreshes the idle clocks. Channel turns count; heartbeat results do not, so a pool kept alive only by its own heartbeats can still sleep. Port of block#7173. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Move the idle clock refresh from the run_harness loop into handle_prompt_result, before the in-flight guard is released, so the existing result-handling tests guard it. Heartbeat results no longer refresh the clocks. Port of block#7173. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Record result_refreshes_idle_clock and its call in handle_prompt_result in the patch inventory, with the wiring test as the behavior check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Agent CLIs start MCP servers lazily through launcher chains such as `npm exec <pkg>` -> `sh -c '<bin>'` -> `node .../.bin/<bin>`, or a node launcher script that runs a package from node_modules. Those chains start after session setup, so they were classified as turn work and held the lazy pool awake forever. A process is now a resident helper when its script lies in a node_modules package with a helper name, or when it has children and every child is a helper (bounded depth). A childless non-helper is still work, so a background shell keeps the worker awake as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Durable JSON record per session scope so a provider session can be resumed after an idle pool sleep, a worker crash, or a harness restart. - Every change runs in a critical section under an exclusive record lock with a bounded wait; a claim writes a fresh lease and later writes only succeed while the record still carries it. - A live harness holds an instance lock for its lifetime, so other processes can tell a dead instance from a live one without PIDs. - Unreadable records move aside to `.corrupt`. A failed write poisons the scope for the process and, inside the critical section on an owned record, moves the record aside to `.stale`. - New options: `--session-store-dir` / `BUZZ_ACP_SESSION_STORE_DIR` and `--no-session-resume` / `BUZZ_ACP_NO_SESSION_RESUME`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Read `agentCapabilities.sessionCapabilities.resume` from `initialize` and add `AcpClient::session_resume`, which sends `session/resume` with the same parameters `session/new` uses (shared `build_session_params`). `resume_failure_code` maps a failed resume to a stable code: `not_found` for -32002, `rpc_<n>`, `timeout`, or `protocol`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
On a session miss, `acquire_session` consults the durable record and resumes the provider session with `session/resume` when safe, otherwise creates a new one with a recorded reason. Each acquisition runs in rounds of decision -> claim -> RPC -> commit with the same lease; a commit that finds the record taken over drops the session it obtained and starts a new round (at most three, then an unpersisted new session). - A resumed session gets its delivery state and turn counter back, so only messages after the pause are delivered and max-turns rotation still counts across resumes. - `invalidate_scope` tombstones the owned record (rotation, errors, model switch, channel removal); `invalidate_all`, used only for dead or unrecoverable workers, marks owned records departed instead. - Delivery success and the turn counter after its increment are written through to the owned record. - The harness opens the store under `<cwd>/.buzz-sessions/<pubkey>` by default and attaches it to every pool and respawned worker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Opening the store no longer touches the disk: the instance lock is created on the first write, so harness runs that never resume a session (tests, short-lived invocations) leave no store directory behind. Every record naming an instance is still written after its lock exists. Registration sweeps lock files of instances that are no longer running, and a store removes its own lock file when dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
- Idle pool sleep marks every idle worker's owned sessions departed before tearing the pool down, so the next turn resumes them. - An owner `!rotate` tombstones the scope's record unconditionally, also when the session lives only on disk after a sleep or respawn; sibling threads keep theirs. - Removal from a channel tombstones every record of that channel. The pool keeps its own store handle, so these work with empty slots. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
A new provider session (or a thread seen for the first time in a session) now always sees the first message in the thread that mentioned the agent, even when it is older than the recent-message window. The scan walks the thread forward through the relay's thread pagination (`depth_limit` filter, composite `(created_at, id)` cursor), so it crosses replies that share one second and stops at the first match. It is bounded to 20 pages of 500 replies; a failed or exhausted scan is reported as not found and pins nothing. A root that mentions the agent needs no pin. The pinned message takes the place of the oldest non-agent reply, right after the root, so the window size is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
The first prompt after a session is created or resumed carries `<session state="new|resumed" reason="..."/>` right before the conversation context, so the agent no longer has to infer a lost session from a truncated thread window. A cold start whose first-mention scan came up empty adds `first_mention="not_found"`. Later turns in the same session carry no marker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Box the record in `LoadOutcome::Found` (clippy::large_enum_variant) and apply rustfmt to the new modules and tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
The rotate-during-RPC tests discarded the record on a timer, which raced with acquisition under parallel test load. Rotate instead when the fake adapter has logged the call that is in flight, a fixed number of times. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
…ments Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Add pure buzz-core types and helpers for session visibility: canonical status identity (http/ws counterparts match, hosts/ports/paths/queries preserved), scope file and lock names for the harness instance directory, a closed state and bounded reason vocabulary, overflow-safe context occupancy percent, history coverage, the version 1 snapshot format with a 16 KiB limit and a shared parse helper, and the buzz-session and buzz-session-info wire tags. No filesystem or async dependencies. Register the module and its export in the fork patch registry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
The supporting tag dropped the incomplete flag, so a known-incomplete cold start without a total looked like "no claim". Emit lower_bound for known-incomplete history or a non-exact total (total and read may be empty), exact only for complete history, and unknown only when there is no completeness claim. Document the encoding and name the info tag version as SESSION_INFO_VERSION. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
The harness now writes a small status snapshot per conversation scope so the CLI can tag the agent's own messages with the session state, the reason for its last transition and the reported context occupancy. - Status runtime: a private instance directory held by an instance lock, atomic bounded snapshots, and one generation per scope guarded by a held generation lock. A successor, retirement or failed write revokes the shared guard and releases the lock even when the old snapshot cannot be removed, so late callbacks and binding clones never rewrite it. A scope whose write failed stops producing status. - run_prompt_task publishes new/resumed with the acquisition reason before any provider prompt, keeps the transition across ordinary turns, and records the unique events supplied to a new session's first prompt from the already fetched context (no extra history walk). - usage_update occupancy is routed by the notification's own session ID before the unchanged Claude cost accounting. - Existing discard/depart paths, owner rotate and channel removal retire status; heartbeats never touch it. - BUZZ_SESSION_STATUS_DIR reaches every adapter spawn and the managed MCP server; the harness owns the variable and drops an inherited value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
A resume failure code outside the codes the harness produces today (including any unrecognised stored wire string) could carry raw adapter or store text, such as a transcript path, into the public status tag. Map only the known codes and rpc_<integer> through; everything else becomes resume_failed:unknown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
When an agent harness advertises its status instance directory in BUZZ_SESSION_STATUS_DIR, newly prepared kind-9 messages carry the buzz-session and buzz-session-info tags of the sender's own session for that destination. The lookup runs in the shared prepare_message path, so messages send and the first messages deliver preparation both get it. - Destination: a reply matches its canonical thread scope or the channel conversation scope (channel policy, DMs); a top-level message matches only the conversation scope. Two current matches are ambiguous. - Read-only and best-effort: the snapshot is used only when the instance and generation locks are still held (nonblocking probes, released at once when acquirable), the identity is the CLI's own relay and key, and scope and instance match. Anything else sends without status tags. - Forum kinds, send-diff and other commands are unchanged. Managed delivery journals the first prepared tags, so retries and late recovery replay them while a new key captures the current status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Agent messages that carry the signed buzz-session and buzz-session-info tags now show a small muted, noninteractive line below the body and above reactions, in channel and thread layouts alike. - Strict parser mirrors the wire contract: closed state vocabulary, bounded reason format, integer percent 0..100 or empty (never 0), supporting tag version 1 with UUID, scope, counts and completeness mode. Invalid or conflicting primary tags show nothing; invalid supporting metadata supplies no counts and no grouping key. - Fixed English copy: New session, Resumed after sleep, Resumed after restart, Resumed after worker failure, resume failure wording per known code, neutral copy for unknown reasons and for the reserved compacted/restarted states. History reads "read N of M messages", "read N of at least M messages" or "Earlier history may be omitted" depending on the reported completeness. - One explanation per loaded transition (signer, channel, scope, transition UUID), on the earliest message by (created_at, id); other messages keep only their percentage, emphasized from 80% with text. - Status is self-reported: only agent messages whose signer is the represented author, read from the original signed tags, so edits, relay-signed events and copied tags cannot claim a session state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Session controls and activityFresh synthetic browser captures of the final menu input later committed as Controls beside an agent replyA known agent retains recovery controls when optional session telemetry is absent. Restart appears firstThe warning is directly below Restart in the first menu. Descriptions use smaller text and a slightly darker color. Choose the history depthAll three options explain what the new session receives. Entire thread explicitly retains the available history limit. Compaction in progressTyped activity updates one operation in place. Compaction outcomesCompleted, failed and unsupported outcomes remain distinct. |
|
Implementation work is complete at
The updated description preserves the broader browser smoke failures, earlier native/provider source provenance, deferred Claude scenario and historical unexplained follow-up observation. The PR remains draft with these validation limits recorded; no merge or comprehensive completion attestation was applied. |
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
07533be to
c6170ac
Compare
… opening threads Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
|
Reviewed the current integrated branch and the two follow-up test fixes at No remaining source-review blockers were found in this scoped re-read. The history assertion now counts actual history reads, requires the exact three-request baseline and compares status enabled/disabled. Asynchronous working/reaction cleanup is excluded. The expired-command case loads chronological historical events before opening the thread; wire-shape, expiry, human-target and manual-actor assertions remain. No retry, deadline or product behavior was changed. Minimalism, elegance and correctness: 9/10 each. All nine current session-controls browser cases passed without retries against byte-equivalent production UI inputs. Earlier complete ACP execution at Known provider limits are recorded in the PR: Claude ACP can initially report a provisional 200k context denominator for a configured 1M model, and Compact's bounded 120-second budget includes waiting for a busy session. The observed busy timeout does not demonstrate a provider compaction RPC failure; successful completion is required before reporting success. Improving adapter window authority and queued-Compact feedback remains optional follow-up work, not a new source-review blocker against the agreed control contract. |
Use filtered provider result metadata to confirm Claude context capacity without trusting provisional ACP sizes. Keep confirmed capacity across ordinary prompts while refreshing the numerator, and invalidate it across model changes, cancellation and generation replacement. Report busy compaction explicitly, allow bounded queue waiting independently of its execution budget, and expire uncertain pending UI state. Keep owner fencing and replay protection. Exercise the hover-only Reply action without forced clicks in the edited-thread fixture. Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Move scoped context actions into the thread composer while preserving drafts, attachments and selection. Schedule one durable status-only turn after a checked restart, using the selected history and conversation language. Fence stale and cancelled follow-ups, preserve queued human input, and keep uncertain model or publication outcomes distinct from reset success. Bound jsdom failure diagnostics and document external process-tree resource limits. Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Add an explicit Russian or English instruction to the status-only request when trusted conversation evidence provides that language. Keep unknown language generic and preserve the selected history. Check the transmitted request across all history depths, including the absence of guessed language directives. Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Thread context controlsBrowser captures retained from the recorded synthetic browser run, with frontend inputs byte-identical to candidate Context actionsAfter choosing a thread agent, the menu shows the selected agent, Back, Restart and Compact. Manage context sits immediately before mention management in the composer. Restart historyThe submenu offers From scratch, Recent history and Entire thread, with the existing restart warning and bounded-history guidance. |
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Final-build human acceptance — confirmed by ownerOn 2026-10-20 the owner reported rebuilding the local app and finding it working, then explicitly requested completion and merge of PR #15. The accepted candidate is This records the owner's reported manual acceptance; it does not claim every checklist row or platform/provider combination was separately executed. Any later issues and improvements are separate tasks. Historical acceptance checklist and its earlier status (superseded)Final-build human acceptance remains openCandidate: Use a disposable thread with an agent you can manage and harmless synthetic facts:
Report build identity, selected history, reset outcome, automatic answer/language, any unexpected task/tool work and PASS/FAIL/UNTESTED. The repository |
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Final-build human acceptance — confirmed by ownerOn 2026-10-20 the owner reported rebuilding the local app and finding it working, then explicitly requested completion and merge of PR #15. The accepted candidate is This records the owner's reported manual acceptance; it does not claim every checklist row or platform/provider combination was separately executed. Any later issues and improvements are separate tasks. Historical acceptance checklist and its earlier status (superseded)Owner-deferred manual acceptance for
|
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
Thread before opening Manage contextCandidate Agent list after opening Manage contextThe same candidate shows the profile avatar treatment used by mention suggestions. Missing images use initials. Both captures come from the passing browser smoke workflow. The separate final-build human checklist is here. The PR remains a draft. |
Signed-off-by: Alexander Leschenko <leschenko@bitrix.ru>
|
Final review of The hosted reaction-order failure was confirmed and corrected without relaxing chronology or spacing. The Windows dependency fetch failure was addressed by the public locator for the identical pinned NetEq commit/tree. Independent rereviews passed both changes. Selected finalization checks and retained full-package evidence are described in the PR body; new hosted CI remains a separate integration result. The owner explicitly requested readiness now and will rebuild/check the app locally later. Those manual steps remain unconfirmed; this review does not attest manual completion. The working local app was not restarted and no extra local browser/model run was performed. |
Accepted completion — 2026-10-20Candidate: The owner rebuilt the local app, reported that it looks working, and then explicitly requested completion and merge. This supersedes the earlier deferred manual-acceptance status. No exhaustive platform/provider checklist execution is inferred from that report. Improvements and any later bugs will be handled as separate tasks. Hosted CI run is complete: 55 checks succeeded, 39 were conditionally skipped, zero failed or unfinished. Final independent source review and retained package/provider evidence remain as documented in the PR body; historical failed attempts and limitations remain preserved. The PR targets the fork's actual default branch, |
|
Merged into The merged tree exactly matches the clean Git merge-tree result for the previous main plus the accepted candidate. Separate device-home changes already in main and their registry entries are preserved. Candidate CI is green; no new test run of the squash commit is claimed. No release, installation or restart of the working app was performed. |








Summary
Agent replies explain whether a provider session is new, resumed, restarted or compacted, how much conversation history was delivered and how much context is occupied when the provider reports it. Restart offers the original assignment, recent messages or available thread history. Checked worker retirement preserves files and protects neighboring foreground threads. Compact operates on the selected existing session and reports queued, successful, failed or unconfirmed results.
The thread composer has Manage context immediately before mention management. It lists the thread agents the user can manage, now with the same profile-avatar/initials treatment as mention suggestions, then opens the selected agent's Restart/depth and Compact actions. Permissions and exact scope are checked again when sending. Draft text, attachments, selection and mention choices are preserved.
A successful Restart schedules one short status-only turn in the replacement session, using the selected history and conversation language without automatically continuing the task. The harness signs the generated answer into the original thread. Its first answer now includes the live session's transition, history, actor and available occupancy tags before signing and durable preparation; unavailable occupancy remains absent and old generations cannot supply it. Delivery retries reuse the same signed bytes. Failure notices retain their separate path. Previously signed messages are unchanged.
The status prompt distinguishes missing task details from missing messages, binds coverage claims to supplied history and leaves unknown prior outcomes unknown. Durable ownership, scope/token fences and prepared bytes prevent automatic replay after an uncertain crash. Later ordinary messages retain their queue entries and use the resulting session.
The Desktop tests also avoid an expensive jsdom diagnostic path by awaiting query-cache rendering and checking boolean absence rather than comparing a live DOM graph with null. Test/build workloads use external memory, swap, deadline and process-tree limits.
Candidate: 94d805f.
Current picker interaction
Retained capture at
7d9f216ea: thread head before opening Manage context. Frontend rendering inputs are unchanged by this finalization:Agent list after opening Manage context in that same passing browser flow:
Final review and validation
Final runtime and UI reviews, including verification of previous blocking findings and both final corrections: PASS. No remaining confirmed important or critical source regression was found. The owner rebuilt the local application, reported that it looks working, and explicitly accepted completion and merging on 2026-10-20. This supersedes the earlier deferred human-acceptance status. This is owner acceptance of the rebuilt app, not a claim that every platform/provider checklist row was individually executed.
The final CI corrections address observed failures on
7d9f216ea:16729881b0a80022a7cbe0acc94c9becf2fad313, version, features and dependency contents. Credential-free fetch of that exact commit succeeded and its complete Git tree matches the already tested source. No dependency upgrade or advisory exception was introduced.Fresh finalization checks completed: fork-tooling suite 80 tests total, four skipped, registry render/validation, Desktop check (three existing warnings), ten file-size-policy tests, Desktop size guard against the pinned fork base, and locked offline Cargo manifest metadata (
--no-deps). Source checks ran on the frozen staged candidate based on7d9f216ea; complete commit promotion preserves all 5,704 file entries.Retained package results remain Rust 2,258 passed / nine opt-in skips, doctests two passed / one ignored, and Desktop 7,190 passed. Complete file-manifest comparison confirms unchanged relevant Rust and Desktop unit inputs. The earlier complete Desktop typecheck remains applicable: its
src, configuration and package-lock inputs are unchanged; the changed E2E spec lies outside the typecheck include. A redundant local typecheck attempt stopped at a 512 MiB V8 heap cap under external containment; that attempt is retained as unsuccessful, not counted as another PASS. The unsupported-Python and missing-local-base attempts are also retained separately.The existing 16/16 browser smoke and four production-invocation mutation/restore proofs retain their original execution provenance. No additional local browser or real-provider run was made during finalization, as requested. Earlier static/build evidence remains scoped to its actual inputs; no new native compilation or full local
just ciPASS is claimed here. Hosted CI for this exact candidate completed successfully: 55 successful checks, 39 conditionally skipped checks, no failed or unfinished checks. See CI run.Real-provider semantic evidence remains Claude 5/5 and Codex 5/5 on 0b31365: three history depths in Russian, an English conversation, Restart during streamed output and a later ordinary turn. Independent review found no invented progress, false absence of supplied messages or task execution in status answers. These earlier provider executions are retained evidence. Retirement evidence is protocol/session replacement plus durable success, not an independent OS process-tree measurement; queue overlap was not exercised.
Native default tests, both all-target Clippy graphs and the complete mesh result (3,857 passed, 23 ignored) retain their historical execution provenance. Finalization changes the NetEq source locator, with exact commit/tree identity independently verified; these older executions are not represented as new runs of the final native manifest. Local mobile remains SKIPPED_BY_OWNER.
All validation workloads used external memory/deadline/process limits, zero swap and confirmed cleanup. The agent did not restart or modify the working Buzz app, databases or installed binaries. The owner separately rebuilt and checked the app. The owner has now authorized merging this PR; no release or installation is part of this closeout.
Limits
Context telemetry is optional, may lag and is not billing usage. Claude capacity comes from unambiguous SDK metadata, preserving genuine 200k and authoritative 1M windows. Resumed Codex sessions retain their previous system prompt; fresh sessions receive current instructions. History discovery is bounded to 10,000 replies and reports incomplete coverage.
Restart does not undo files or work outside the managed process topology. A neighboring Unix background hold may delay replacement for up to ten minutes. Queued compaction is in memory; durable Started/terminal records preserve uncertain executions. After controller death an uncertain status attempt is not automatically rerun or published; a fresh Restart explicitly requests another attempt. A transport submission already sent cannot be retracted.
Language inference recognizes Russian, English or unknown. Provider tool rejection is not a universal native-tool sandbox. One earlier Claude finished-parent/live-native-child row remains owner-deferred; the previously untraced extra Codex follow-up remains qualified by its original evidence. Real macOS, Windows and ARM acceptance remains unverified here. Startup/idle-policy work in #7 and fan-out recovery in #25 remain separate. Refs #6, #7.
Accepted completion
94d805f925c1f0d9ee959d5d8d7199ea4559322dis green.buzz-review-completed