stuck task issue
This commit is contained in:
+1
-1
@@ -215,7 +215,7 @@ Agents don't commit; byre does. Each `user.message` event that starts a turn rec
|
||||
|
||||
A session runs on one of three vendors, picked by the model it is created with (`workflow.VendorOf` in `internal/workflow/workflow.go`): `gpt-*` models run on Codex (`codex app-server`), `gemini-*` models on Antigravity (`agy`), every other model on Claude Code. The UI's model list is `models` in `mac/Sources/Byre/Composer.swift`; a live session's picker offers only its own vendor's models, and the API refuses (400) a switch to another vendor's model. All vendors sit behind `agentProcess` (`internal/agents/process.go`), and all end a turn through the same `turnEnded`, so commits, turn summaries, snapshots, handoffs, merges and deliveries don't depend on the vendor.
|
||||
|
||||
For Claude, byre chooses the session ID (`vendor_session_id`) up front. Codex picks its own thread id: a Codex session is created with an empty `vendor_session_id`, and byre stores the thread id when its first process starts (`startCodex`); a session with an id resumes it with `thread/resume`. Antigravity does the same with its conversation id, which `agy` reports in its `init` line at launch (`startAntigravity`), resumed with `--conversation`. Processes start on demand: the first message after a restart resumes the session (`--resume` for Claude). Sessions are `running` from the moment a turn is sent until it ends (Claude's and Antigravity's `result`, Codex's `turn/completed`); on startup, every session still marked `running` was cut off mid-turn, and byre resumes it and sends a message telling the agent to re-check the working tree. Stopping the server kills all agent processes. Merging or discarding a task kills its sessions and marks them `completed`.
|
||||
For Claude, byre chooses the session ID (`vendor_session_id`) up front. Codex picks its own thread id: a Codex session is created with an empty `vendor_session_id`, and byre stores the thread id when its first process starts (`startCodex`); a session with an id resumes it with `thread/resume`. Antigravity does the same with its conversation id, which `agy` reports in its `init` line at launch (`startAntigravity`), resumed with `--conversation`. Processes start on demand: the first message after a restart resumes the session (`--resume` for Claude). Sessions are `running` from the moment a turn is sent until it ends (Claude's and Antigravity's `result`, Codex's `turn/completed`); on startup, every session still marked `running` was cut off mid-turn, and byre resumes it and sends a message telling the agent to re-check the working tree. Stopping the server kills all agent processes. Merging or discarding a task kills its sessions and marks them `completed`. Every agent CLI starts in its own process group, and killing one sends `SIGKILL` to the whole group. That way a launcher that runs the real agent as its child, along with the agent's MCP servers, can't keep holding byre's stdout pipe, which would leave the kill waiting forever for end of file.
|
||||
|
||||
Every session gets byre's guidance (`guidance` in `internal/agents/guidance.go`): ask questions with `ask_human`, end each turn with a final message that summarizes it (including every departure from the plan), put everything the human needs in a question's context since that turn has no summary, don't commit (byre commits each turn), and don't push, touch other branches or run git maintenance. A task agent's guidance also names its port range, tells it to do the setup AGENTS.md describes in a new worktree (see "Worktree setup and ports"), and quotes the task's title, which may be all the human typed when creating the task. A task started from a `TODO.md` item gets one more line naming the item and telling the agent to remove it from `TODO.md` in its worktree when the work is done (`taskGuidance`; see "TODO"). A subtask gets one naming its parent and saying that its final messages go to the parent's agent (`subtaskGuidance`; see "Subtasks"). For Claude it is passed as `--append-system-prompt`, which Claude Code records on a session's first request and reuses on resume, so a running session keeps the text it started with; a `UserPromptSubmit` hook also attaches it as `additionalContext` to every user turn, which reaches existing sessions too. For Codex it is the thread's `developerInstructions`, and every `turn/start` also carries it as `additionalContext` under the key `byre`. For Antigravity it is an always-on rule in the session's plugin (`rules/AGENTS.md`, with the notes in `rules/notes.md`), written when byre starts the session's process, and every message also starts with it in `<system-reminder>` tags. The guidance names byre's tools vendor-neutrally ("byre's ask_human tool"): Claude sees them as `mcp__byre__<name>`, Codex as `<name>` in the namespace `byre` (Codex reserves the `mcp__` prefix), Antigravity as `<name>` on the MCP server `byre_byre`, called through its `call_mcp_tool` (recorded as `mcp__byre__<name>`).
|
||||
|
||||
|
||||
@@ -334,6 +334,7 @@ func (s *Session) spawnLocked() error {
|
||||
"GIT_COMMITTER_EMAIL="+s.opts.GitEmail,
|
||||
)
|
||||
cmd.Env = append(cmd.Env, s.opts.Env...)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
@@ -428,7 +429,7 @@ func (s *Session) restartLocked() error {
|
||||
select {
|
||||
case <-p.done:
|
||||
case <-time.After(10 * time.Second):
|
||||
p.cmd.Process.Kill()
|
||||
syscall.Kill(-p.cmd.Process.Pid, syscall.SIGKILL)
|
||||
<-p.done
|
||||
}
|
||||
return s.spawnLocked()
|
||||
@@ -567,7 +568,7 @@ func (s *Session) Kill() {
|
||||
s.mu.Unlock()
|
||||
|
||||
if p != nil {
|
||||
p.cmd.Process.Kill()
|
||||
syscall.Kill(-p.cmd.Process.Pid, syscall.SIGKILL)
|
||||
<-p.done
|
||||
}
|
||||
s.die()
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"os/exec"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"g.fc.run/ai/byre/internal/mcp"
|
||||
@@ -251,6 +252,7 @@ func Start(ctx context.Context, opts Options) (*Session, error) {
|
||||
s.cmd.Dir = opts.Cwd
|
||||
s.cmd.Stderr = os.Stderr
|
||||
s.cmd.Env = buildEnv(opts)
|
||||
s.cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
|
||||
s.stdin, err = s.cmd.StdinPipe()
|
||||
if err != nil {
|
||||
@@ -505,7 +507,7 @@ func (s *Session) Close() error {
|
||||
select {
|
||||
case <-s.done:
|
||||
case <-time.After(5 * time.Second):
|
||||
s.cmd.Process.Kill()
|
||||
syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL)
|
||||
<-s.done
|
||||
}
|
||||
|
||||
@@ -513,7 +515,7 @@ func (s *Session) Close() error {
|
||||
}
|
||||
|
||||
func (s *Session) Kill() {
|
||||
s.cmd.Process.Kill()
|
||||
syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL)
|
||||
<-s.done
|
||||
s.cmd.Wait()
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"g.fc.run/ai/byre/internal/mcp"
|
||||
@@ -159,6 +160,7 @@ func spawn(opts Options) (*Session, error) {
|
||||
cmd.Dir = opts.Cwd
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Env = buildEnv(opts)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
|
||||
stdinR, stdin, err := os.Pipe()
|
||||
if err != nil {
|
||||
@@ -536,7 +538,7 @@ func (s *Session) Close() error {
|
||||
select {
|
||||
case <-s.done:
|
||||
case <-time.After(5 * time.Second):
|
||||
s.cmd.Process.Kill()
|
||||
syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL)
|
||||
<-s.done
|
||||
}
|
||||
|
||||
@@ -544,7 +546,7 @@ func (s *Session) Close() error {
|
||||
}
|
||||
|
||||
func (s *Session) Kill() {
|
||||
s.cmd.Process.Kill()
|
||||
syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL)
|
||||
<-s.done
|
||||
s.cmd.Wait()
|
||||
}
|
||||
@@ -577,7 +579,7 @@ func (s *Session) write(m message, deadline time.Time) error {
|
||||
n, err := s.stdin.Write(append(b, '\n'))
|
||||
if n > 0 && errors.Is(err, os.ErrDeadlineExceeded) {
|
||||
slog.Error("codex stopped reading its stdin mid-line; killing it", "pid", s.cmd.Process.Pid, "written", n, "len", len(b)+1)
|
||||
s.cmd.Process.Kill()
|
||||
syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user