Skip to content

Add agent teams, owner chat recovery and goal continuation improvements - #24

Open
LeZergan wants to merge 9 commits into
devnoname120:mainfrom
LeZergan:pr/agent-teams-runtime
Open

LeZergan wants to merge 9 commits into
devnoname120:mainfrom
LeZergan:pr/agent-teams-runtime

Conversation

@LeZergan

@LeZergan LeZergan commented Nov 12, 2026 •

Copy link
Copy Markdown

Summary

Split out of #20, as requested. This PR has the native runtime work: agent teams, owner-chat recovery, goal continuation and the browser settings that use #22.

Stacked on #22 and #23. This branch contains their commits first, so GitHub shows a cumulative diff. Please review only the last two commits: "Add agent teams, owner chat recovery and goal continuation improvements" and "Address review: Windows workspace paths, non-Windows lint and test fixtures". After #22 and #23 merge I'll rebase, and this PR will show only its own changes. The pieces share supervisor.rs, store.rs, server.rs and owner_chat.rs too heavily to make an independent branch that compiles on its own.

New features are off by default unless noted.

Changes

Agent teams and AI agent control (agentChat.features.agentTeams, externalAgentControl)

  • Parent and worker chats, each worker with its own goal. New tools: agent_spawn, agent_status, agent_report, agent_control and agent_message.
  • Control is revision-checked (expectedRevision). A chat can only control its own recorded children, and workers can't control siblings or their parent.
  • Every worker goal and get_agent_brief includes a coordination agreement (docs/AGENT_COORDINATION.md).
  • One agent connection works over MCP, HTTP and a dependency-free JavaScript helper. The service publishes it in ~/.codexify/agents/ (connection.json, client.mjs). See docs/CODING_APP_AGENTS.md.
  • Optional auto mode and default delegation.

Owner chat

  • An Agents panel and agent folders (agentFolders).
  • A working-tree changes counter in the chat header. It is optional, rate-limited and never blocks chat.
  • A faster chat listing (owner_chat/listing.rs), with reads bounded in parallel.
  • Opt-in conversation recovery (conversationRecovery) replaces an inaccessible conversation without losing the task. It takes a recovery lease, then prepares the new chat and checks the real account before proceeding.

Goals and continuation

  • An infinite goal stops only when the same concrete blocker persists across three distinct goal turns spanning at least 60 seconds (goal_blocking.rs).
  • Offline detection counts native calls and running commands as activity, using upstream's configurable offline timeout.
  • Turn handoffs reset the previous owner's renewal clock.
  • When the browser observer fails late, an agent's acknowledgment of the exact message batch still settles the uncertain send, even while another page owns the scan.
  • Optional infinite retries (retryTimings.infiniteRetries).
  • Agent instructions changed: concurrent workspace edits no longer end the turn, and there's a new section on execution and tool speed.

Browser runtime settings (uses #22)

  • agentChat.automation.browserActionLanes (1–4) enables parallel action pages, with one action per conversation enforced natively.
  • Browser simplicity (Off / Simplified / Bones) and opt-in recovery enhancements, both passed to the companion at launch.
  • Fast setup applies the recommended preset in one save. It's available in Settings and as codexify config fast-setup. Quickstart fills it in only for missing fields.
  • Behaviour change: the healthy conversation-load interval now defaults to 1 second (was 180). HTTP 429 and Retry-After back-off still apply, and an explicitly saved interval is kept.

Tooling and docs

  • scripts/monitor-agent-performance.mjs, owner-chat UI smoke scripts, README and docs/REFERENCE.md updates.

Testing (Windows 11, full merged tree)

  • cargo test: 1658 passed. The failures that remain also fail on unmodified main on Windows, so they come from the environment:
    • doctor_cli (a Codexify service is installed on this machine)
    • existing_worktrees and workspace_resume (\\?\ path spelling)
    • chat_attachments
    • the exec_command output-budget test
  • The tests in agent_automation_supervisor, agent_automation_parallel and config all pass.
  • cargo fmt --check passes.
  • cargo clippy --all-targets -D warnings: no findings in the new code. The remaining warnings are Windows-only (unused variables in upstream's code behind cfg(unix)).
  • Browser companion: bun test 309 pass / 0 fail, and tsc --noEmit passes.

🤖 Generated with Claude Code

Adds an optional pool of up to four fixed ChatGPT pages in one private Chrome
profile (one action per conversation; maintenance drains every page; shared
429 back-off and Send spacing), with optional health fields describing
capacity. The companion still defaults to one page.

Also recovers orphaned Windows Chrome, normalizes renderer priority, waits for
editor hydration instead of treating a missing editor as sign-out, journals
owned unsent drafts, bounds read-only DOM observations and passive health
polls, persists Retry-After across restarts, and adds optional Simplified and
Bones presentation levels. Upstream's admission, pairing-window reservation,
chat-mode, conversation-limit and transcript rate-limit handling are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@devnoname120 devnoname120 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the final commit 20be346 separately from its #22/#23 prerequisites, focusing on worker ownership/revision checks, durable controls, recovery leases, continuation fencing, native activity tracking, settings and the external agent client. Requesting changes; this stack is not ready to merge.

The Windows worktree path comparison is a functional blocker for the new agent client. The new native code also fails the non-Windows lint gate, and the split still omits a helper imported by its tests. Inline comments describe each issue.

Validation at this head:

  • Clean library run: 934 passed, 3 failed, 1 ignored. Both recovery-lease failures reproduce the macOS CI descriptor-directory permission error. The third failure is owner_api_lists_sends_and_reads_the_same_persisted_chat: its new agentTeam assertion expects metadata although the fixture leaves agentFolders disabled. Align the fixture/contract before treating the suite as passing.
  • Selected agent_connection, agent_automation_parallel, agent_automation_supervisor and turn_renewal integration suites: 182 passed, 1 ignored.
  • cargo fmt --all --check passed. cargo clippy --all-targets -- -D warnings failed with the new non-Windows loop/mutability diagnostics and the inherited unused PendingBuffer helper.
  • node --test scripts/test-agent-client.mjs fails before running tests because its imported helper is missing; the separate monitor tests passed all 9 cases.
  • Current CI fails browser packaging/native account-maintenance tests inherited from #22, the macOS owner-chat tests above, and the retry-settings UI assertion in both Chromium and WebKit (9 controls versus the expected 8). The Windows service job passes. These are not all Windows-environment failures.

The positive evidence is useful: the selected scheduling/continuation integration suites pass and the code contains explicit ownership checks, compare-and-set guards and tests for uncertain sends. However, the final commit adds roughly 16,000 lines across agent teams, recovery, settings and UI, beyond the prerequisite browser/execution changes. I would land the corrected prerequisites first, then rebase this PR and rerun its full native/browser/UI validation. The new client and recovery features need the inline defects and failing contracts resolved before approval.

Comment thread src/agent_automation/agent_clients.rs Outdated
.bindings
.selected_project_root(&self.config, &task)?
.ok_or("Coding-app team workspace is missing; retry setup")?;
if root != std::path::Path::new(&record.project_path) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Normalize the saved workspace before comparing it with the binding

With createWorktree=true on Windows, a new managed worktree is returned under the plain drive path produced by prepare_worktrees_root/strip_verbatim_prefix. Setup saves that spelling into record.project_path at line 395. client_task immediately reloads the binding through selected_project_root, whose canonical_binding_dir uses fs::canonicalize and returns the extended-length spelling. Path equality treats C:... and \?\C:... as different, so setup saves initialized=true and then fails with "Team workspace changed" even though the workspace has not moved; retries/status/control keep failing on the saved record. Store and compare canonical paths consistently, and cover setup plus reconnect with createWorktree=true on Windows. This follows from the two path-producing code paths; I did not execute the Windows runtime locally.

Comment thread src/owner_chat.rs Outdated
let mut temporary = prepare_runtime(parent, &info)?;
#[cfg(windows)]
let mut retry_delay_ms = 10;
loop {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Keep Windows-only retry loops out of non-Windows builds

The only continue in this loop is behind cfg(windows), so on macOS/Linux the loop always breaks or returns on its first iteration. cargo clippy --all-targets -- -D warnings fails with clippy::never_loop here and unused_mut for temporary above it. The new retry loop in agent_automation/store.rs:4212 has the same non-Windows problem. I reproduced both at this head on macOS. Use a platform-specific retry implementation or conditional loop/mutability so the ordinary non-Windows persist path remains warning-free. The inherited PendingBuffer::is_empty warning from #23 is separate.

import {pathToFileURL} from 'node:url';
import test from 'node:test';
import {setTimeout as delay} from 'node:timers/promises';
import {monitorTeams} from './windows-autowork-team-status.mjs';

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Include the helper required by the new agent-client tests

This imports windows-autowork-team-status.mjs, but that file is only added by #21. The declared #22/#23 stack in this branch does not contain it. Running node --test scripts/test-agent-client.mjs fails at module load with ERR_MODULE_NOT_FOUND, so none of the new JavaScript client tests execute. Put the shared helper with its consumers, remove the cross-PR test dependency, or explicitly include #21 in the stack/merge requirements and validate the resulting tree.

Comment thread src/owner_chat/recovery.rs Outdated
.unwrap();
config.memory.dir = Some(root.path().join("metadata").display().to_string());
config.markdown_chat.automation.descriptor_path =
root.path().join("descriptor.json").display().to_string();

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Make the recovery fixture satisfy the real Unix descriptor boundary

The fixture writes the descriptor directly under the temporary root and later sets only the file to 0600. Its parent is not established as a private directory before the real BrowserCompanionClient is used. Both recovery_lease_prepares_before_passive_health_and_waits_for_real_account_proof and recovery_lease_skips_its_task_while_unrelated_scans_continue fail with 409 "Browser companion descriptor directory must be private to the current user" in macOS CI and in my local run. Create/chmod a private fixture descriptor directory on Unix rather than weakening the production validator, so these tests actually reach and verify recovery commit/lease behavior.

- After clicking the connector row, place the composer caret after the
  retained chip. A pointer click left it at the typed query, so the prompt
  was inserted before the chip.
- A window operation queued behind a predecessor no longer expires while
  that predecessor is still within its own budget (connector refresh can
  legitimately outlast a plan or reveal request). It still expires once the
  predecessor overruns.
- Package staging accepts either spelling of a linked scratch directory
  (macOS /var -> /private/var) and still rejects an escaped real parent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@LeZergan
LeZergan force-pushed the pr/agent-teams-runtime branch from 20be346 to f917a61 Compare November 13, 2026 10:39
@LeZergan

Copy link
Copy Markdown
Author

Thanks for the review. I rebuilt the stack on the fixed #22 (810adf7) and #23 (a6f2ad7, 6ddc019), and every finding is addressed in the new top commit:

Windows workspace comparison (agent_clients.rs)

  • Setup now saves the canonical spelling of the selected workspace.
  • client_task compares canonicalized paths on both sides, so a createWorktree=true worktree (plain C:\… from strip_verbatim_prefix) matches the binding's \?\C:\…. Records already saved in the plain spelling also match.
  • New unit test workspace_comparison_accepts_both_spellings_of_a_managed_worktree builds both spellings exactly as the two code paths do. It also checks that a different or removed directory is still rejected.

Non-Windows lint

  • Both retry loops (owner_chat.rs runtime credentials and store.rs state publish) now go through one helper, crate::persist::persist_replacing. It has a Windows backoff body and a plain persist body elsewhere, so there is no never_loop or unused_mut.
  • cargo clippy --target x86_64-unknown-linux-gnu --all-targets -- -D warnings now passes on this branch. That includes the inherited PendingBuffer::is_empty, which is now #[cfg(test)] in Keep command output and Git checkpoints reliable on Windows #23.

test-agent-client.mjs helper

Recovery fixtures

  • The recovery tests now put the descriptor in a private (0700 on Unix) directory, so they reach the real lease, commit and preparation paths without weakening the validator.

owner_api_lists_sends_and_reads_the_same_persisted_chat

  • The fixture already enabled agentFolders. The actual Unix problem was that it wrote automation.json with std::fs::write (0644), and the store reads only private records. The listing therefore got "Automation state is unavailable" and returned no agentTeam. The fixture now chmods the record to 0600.

Retry settings UI

  • The ninth control is the new retryTimings.infiniteRetries switch. The test now expects 9 and asserts that switch explicitly. The Chromium case passes locally.

The activity registry moved here from #23, because its only consumers (agents.rs, supervisor.rs) are in this PR.

I agree #22 and #23 should land first. After they merge, I'll rebase so this PR shows only its own commit.

LeZergan and others added 7 commits November 13, 2026 21:20
The browser companion's unconfigured spacing between conversation loads
goes back to the upstream 180 seconds. Lane tests now configure the
one-second interval they exercise explicitly, a new test pins the default,
and the timing tests run on a frozen clock so they are not load-dependent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
exec_command keeps a session addressable until its final output and exit code
are polled, retains final results until read, and lets an owner message end
only the output wait. Git checkpoints skip repeated legacy-namespace probes and
drain stderr under the command deadline. Paths drop the \?\ verbatim prefix
before reaching Git, the native server runs at Normal priority, and the
supervisor holds an OS lock for its lifetime.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the conversation command-activity registry to the agent-teams change
that consumes it, and compile PendingBuffer::is_empty only for tests now
that the reaper no longer treats an empty buffer as an acknowledgement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Admission counted every retained session against maxSessions, so a client
that never drained final results could hold all slots for the idle timeout.
Only running processes now block admission: when the map is full, the least
recently used finished-but-unread results are evicted (and logged) to admit
the new command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent teams (off by default): parent and worker chats with per-worker goals,
revision-checked agent_control, agent_message, agent_status and reports, plus
one agent connection over MCP, HTTP and a dependency-free JavaScript helper.

Owner chat: an Agents panel and folders, a working-tree changes counter, a
faster chat listing, and opt-in recovery that replaces an inaccessible
conversation. Goals: infinite goals stop only when the same concrete blocker
persists across three turns and 60 seconds; native offline detection counts
running native calls and commands; turn handoffs reset per-owner clocks.

Browser runtime: up to four parallel action pages (browserActionLanes),
Browser simplicity and recovery settings, Fast setup in Settings and
`codexify config fast-setup`, and a one-second healthy conversation-load
interval (HTTP 429 and Retry-After back-off still apply).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…xtures

- Save the canonical workspace spelling at setup and compare canonical
  paths, so a createWorktree=true worktree (plain C:\ path) matches its
  binding's extended-length path.
- Route both Windows-only retry loops through persist::persist_replacing,
  so non-Windows builds have no never_loop/unused_mut.
- Keep the command-activity registry with its consumers here (moved out
  of the standalone execution change).
- Include windows-autowork-team-status.mjs, used by test-agent-client.mjs.
- Recovery fixtures use a private descriptor directory; the owner API
  fixture writes its team record privately on Unix.
- The retry settings UI test expects the new infiniteRetries switch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…reaker

Defaults for configurations that omit them go back to the upstream values:
one browser action lane, 180-second conversation-load spacing, and
delegation off unless enabled globally or per team. Fast setup and
quickstart no longer turn on Bones/Simplified, infinite retries, turn
renewal or a faster load interval; each stays a separate opt-in.

agentChat.features.maxActiveWorkers (default 4, 1-32) is a hard per-project
cap on active workers; spawn requests that would exceed it are refused,
while replays of accepted requests stay idempotent. Agent status reports
the limit.

The supervisor now counts dispatched loads, accepted sends, recoveries,
renewals and worker turns (owner /api/health automationActivity). When
automatic work exceeds its 10-minute limit the breaker opens for 15
minutes and holds recoveries, renewals and worker turns in the queue;
owner messages and setup are never held.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@LeZergan
LeZergan force-pushed the pr/agent-teams-runtime branch from f917a61 to 9a7851b Compare November 13, 2026 18:19
@LeZergan

Copy link
Copy Markdown
Author

Follow-up review addressed in 9a7851b. The branch is rebased onto the updated #22 and #23, so review the last commit.

Defaults restored for configurations that omit them:

  • browserActionLanes = 1
  • conversationLoadIntervalSeconds = 180
  • defaultDelegation = false. Delegation needs this global switch or a per-team grant.

Fast setup / quickstart no longer enable Bones/Simplified, infiniteRetries, turn renewal, or a faster load interval. Each one is now a separate explicit opt-in, and a test asserts the preset never touches them.

Hard active-worker cap: new agentChat.features.maxActiveWorkers, default 4, range 1–32, counted across all teams in a project. A spawn request that would exceed it is refused without recording any worker. Replays of already accepted requests stay idempotent. agent_status reports capacity.limit, and the worker brief no longer says there is no limit.

Counters and circuit breaker:

  • The supervisor counts dispatched loads, accepted sends, recoveries (goal recovery plus replacement conversations), renewals and worker turns.
  • They are exposed as automationActivity on the owner /api/health: lifetime totals, a 10-minute window, the limits and the breaker state.
  • When automatic work reaches its limit within 10 minutes (180 loads, 12 recoveries, 12 renewals or 60 worker turns), the breaker opens for 15 minutes.
  • While it is open, recoveries, renewals and worker turns stay queued. Owner messages, pairing and new-chat setup are never held.
  • capacity.automaticWorkHeldUntilMs tells coordinators when held work resumes.

The loads counter counts dispatched actions, which is an upper bound on real document loads (same-page reuse makes none). Actual load spacing is still enforced by the companion.

Validation:

  • Linux-target clippy --all-targets -D warnings is clean.
  • Lib tests: 922/923. The one failure is the known Windows baseline requested_output_budget_cannot_exceed_server_policy, which also fails on unmodified main. A few loopback-HTTP tests flake under parallel load on this machine but pass when rerun serially.
  • I couldn't run the settings UI Playwright test here because Playwright isn't installed at the repo root.

This branch has not been deployed

No deployments
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.

2 participants