Skip to content

feat(devkitd): migrate interactive agent start to the daemon - #406

Merged
codeaholicguy merged 10 commits into
mainfrom
feature-devkitd-agent-start
Oct 23, 2026
Merged

codeaholicguy merged 10 commits into
mainfrom
feature-devkitd-agent-start

Conversation

@codeaholicguy

Copy link
Copy Markdown
Owner

Summary

Migrates interactive agent start from the TypeScript agent-manager into the Rust devkitd daemon over the Unix-socket RPC interface. daemon.db's existing agents table becomes the registry source of truth (observed process state + registration metadata merged, no second registry table). Both tmux and herdr runtimes supported; durable mode unchanged.

Rust

  • proto.rs — Response.code/details (backwards-compatible), AgentStartParams/AgentStartResult, ts-rs exports
  • store.rs + store/registry.rs — registry columns on agents + partial unique index on live names; three snapshot rules (register-before-sweep adoption, pid-reuse full delete, gone_at tombstones); legacy agents.db boot-import + dual-write bridge for unmigrated consumers (kill/send/rename/open)
  • devkit-harness/start_profiles.rs — MatchSpec port of all 11 HARNESS_RUNTIME_PROFILES matchers
  • devkitd/runtime/ — TmuxRuntime + HerdrRuntime with shared Availability enum, arg order and runtimeRef shape identical to TS clients
  • devkitd/rpc/ — agent.start serialized under start_mutex, coded errors, agent.started event, registry overlay in agent.list, post-start sweep; parse_params helper for future agent.* ops

TS

  • DaemonClient.startAgent() + DaemonRpcError{code,details}; autostart handles spawn-lock/readiness/log-rotation
  • start.command.ts — daemon-first routing; falls back to the existing TS path only on unreachable daemon or unknown method (pre-agent.start devkitd)
  • --debug now traces routing decisions end-to-end (ai-devkit:agent:start namespace); NO_AI_DEVKIT_BIN envvar forces the binary-missing fallback for testing

Test plan

  • cargo test --workspace — 133 tests (30 devkit-core, 80 devkit-harness, 23 devkitd)
  • cargo clippy --workspace --all-targets -- -D warnings clean; cargo fmt --check clean
  • cli agent tests — 106; daemon-client — 16 (incl. real-socket integration)
  • Live e2e: real devkitd + tmux + claude — start → register → agent.list overlay → agent.started event → kill → tombstone + disappeared
  • npx ai-devkit lint --feature devkitd-agent-start — 12/12

Known deltas (documented in implementation doc)

  • Client disconnect mid-start: daemon completes + registers the agent (vs. today's orphaned-session outcome — strictly better)
  • agent.started + agent.appeared both fire — expected double-announce
  • Legacy agents.db bridge remains until kill/send/rename/open/pin RPCs land
  • tmux PATH for systemd-installed daemons noted as follow-up

Add agent.start RPC so devkitd owns spawning and registration for
tmux and herdr runtimes while daemon.db becomes the registry source
of truth. The CLI routes through the daemon when reachable and falls
back to the existing TS path on connect-failure or older devkitd.

- proto: Response gains code/details; AgentStartParams/Result exported
- store: agents table gains registry columns + unique-name partial
  index; registered-before-swept adoption, pid-reuse invalidation,
  named tombstones; legacy agents.db import + dual-write bridge
- devkit-harness: StartProfile + MatchSpec port of the TS matchers
- devkitd: TmuxRuntime (session mgmt + 5-stable-poll pid resolution),
  HerdrRuntime (busy retry, process-info poll), start_mutex-serialized
  agent_start handler, agent.started event, registry overlay in sweeps
- daemon-client: DaemonRpcError, startAgent() with start-scoped timeout
- cli: daemon-first start.command with identical error rendering
- runtime/testkit.rs: single shared FixtureDir + fake tmux/pgrep/ps/herdr
  scripts, replacing three near-identical copies (~170 lines)
- Remove speculative AgentRuntime::provider() and unused Store::live_name
- agent_start maps StartError via Display instead of duplicating message
  text; HerdrCall::detail() folds the availability error mapping
- Fold agent_start::run wrapper into the dispatch arm
…namespace

- devkit-core/store.rs -> store.rs + store/registry.rs: snapshot/events stay
  in the parent; registration lifecycle (register_start, lookup_name,
  unregister, prune_dead, registry_overlays) and the legacy agents.db
  bridge move to the child module
- devkitd/agent_start.rs -> rpc/agent/start.rs; shared agent-op helpers
  (name validation/generation, pid_alive) live in rpc/agent/mod.rs and
  StartRpcError becomes RpcError in rpc/mod.rs for the next agent.* ops
- Runtimes moves to runtime/mod.rs beside the impls it bundles
- Per-test legacy db filenames fix a shared-fixture parallel-test race

No wire, schema, API, or behavior changes.
…eading

- Availability {ok, reason?, detail?} allowed ok=true+reason=Some invalid
  states; Ok/Missing(detail)/Unreachable(detail) removes them and keeps
  the wire reason strings in one place
- import_legacy_agents -> read_legacy_rows + attach-or-tombstone loop:
  the query is now a named step separate from the merge
- Daemon::parse_params<T> collapses the serde param-validation block each
  dispatch arm repeats; agent.start uses it, future agent.* ops get
  coded invalid_params for free (Err is Box<Response> per clippy)
- devkitd gains serde workspace dep for the DeserializeOwned bound
- implementation doc updated for the rpc/ + store/registry.rs layout
…IT_BIN

- ensureDaemon/resolveDaemonBinary accept an onDebug callback; the CLI
  feeds it the ai-devkit:agent:start logger so --debug shows socket
  connect, autostart spawn, lock contention, timeouts, and which routing
  path was taken (daemon / fallback / coded error / transport failure)
- NO_AI_DEVKIT_BIN forces "no binary found" so the fallback path is
  exercisable end-to-end without uninstalling devkitd; takes precedence
  over DEVKITD_BIN so the test knob can't be overridden accidentally
- daemon-client stays zero-dependency: debug lines flow through the
  caller's logger rather than a direct debug dep
- runtime::spawn_output retries os error 26 (text file busy): exec of a
  just-written executable fails transiently on Linux/overlayfs (CI test
  fixture scripts) and when a binary is being atomically replaced
- the 4 daemon-debug files were prettier-formatted; the repo checks with
  oxfmt — reformatted to match CI
rpc/mod.rs -> rpc.rs, rpc/agent/mod.rs -> rpc/agent.rs,
runtime/mod.rs -> runtime.rs — matches store.rs + store/registry.rs
@codeaholicguy
codeaholicguy merged commit dad3de5 into main Oct 23, 2026
8 checks passed
@codeaholicguy
codeaholicguy deleted the feature-devkitd-agent-start branch October 23, 2026 11:57
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