Run one user turn until the assistant stops or returns a tool call. Tool * results are appended to the transcript and the loop continues, which gives * the model native DSML tool iteration without a client/server protocol. */
| 7633 | |
| 7634 | static void agent_worker_note_terminal_mode_may_have_changed(agent_worker *w) { |
| 7635 | if (!w) return; |
| 7636 | pthread_mutex_lock(&w->mu); |
| 7637 | w->raw_mode_needs_restore = true; |
| 7638 | pthread_mutex_unlock(&w->mu); |
| 7639 | } |
| 7640 | |
| 7641 | static void agent_bash_finalize(agent_bash_job *job, int status) { |
| 7642 | agent_bash_drain(job); |
| 7643 | if (job->pipe_fd >= 0) { |
| 7644 | close(job->pipe_fd); |
| 7645 | job->pipe_fd = -1; |
| 7646 | } |
| 7647 | if (job->tmp_fd >= 0) { |
| 7648 | close(job->tmp_fd); |
| 7649 | job->tmp_fd = -1; |
| 7650 | } |
| 7651 | if (WIFEXITED(status)) job->exit_status = WEXITSTATUS(status); |
| 7652 | else if (WIFSIGNALED(status)) job->exit_status = 128 + WTERMSIG(status); |
| 7653 | else job->exit_status = -1; |
| 7654 | job->running = false; |
| 7655 | /* A child can still open /dev/tty directly and alter terminal state even |
| 7656 | * though its stdin is /dev/null. Ask the UI thread to verify raw mode at |
| 7657 | * a safe point instead of touching linenoise from the worker path. */ |
| 7658 | agent_worker_note_terminal_mode_may_have_changed(job->worker); |
| 7659 | } |
| 7660 | |
| 7661 | /* Drain available output, notice process exit, and enforce timeout. This is |
| 7662 | * called opportunistically by status/wait/compaction instead of a background |
| 7663 | * reaper thread, keeping all bash job state owned by the agent worker. */ |
| 7664 | static void agent_bash_poll(agent_bash_job *job) { |
| 7665 | if (!job || !job->running) return; |
| 7666 | agent_bash_drain(job); |
| 7667 | |
| 7668 | int status = 0; |
| 7669 | pid_t rc = waitpid(job->pid, &status, WNOHANG); |
| 7670 | if (rc == job->pid) { |
| 7671 | agent_bash_finalize(job, status); |
| 7672 | return; |
| 7673 | } |
| 7674 | if (rc < 0 && errno != EINTR) { |
| 7675 | job->exit_status = -1; |
| 7676 | job->running = false; |
| 7677 | if (job->pipe_fd >= 0) { |
| 7678 | close(job->pipe_fd); |
| 7679 | job->pipe_fd = -1; |
| 7680 | } |
| 7681 | if (job->tmp_fd >= 0) { |
| 7682 | close(job->tmp_fd); |
| 7683 | job->tmp_fd = -1; |
| 7684 | } |
| 7685 | agent_worker_note_terminal_mode_may_have_changed(job->worker); |
| 7686 | return; |
| 7687 | } |
| 7688 | if (now_sec() - job->start_time >= job->timeout_sec) { |
| 7689 | job->timed_out = true; |
| 7690 | kill(-job->pid, SIGKILL); |
| 7691 | kill(job->pid, SIGKILL); |
| 7692 | while (waitpid(job->pid, &status, 0) < 0 && errno == EINTR) {} |
no test coverage detected