Repository navigation
stagent v0.10.0
Added
github.status {items[{repo, kind, number, branch?}]}(Windows,
capabilitygithub) reports the state gh gives each issue or pull
request (OPEN,CLOSED,MERGED), and for an issue withbranch
the state of that branch's pull request aspr_state(the answer
echoingbranch), so the app can
mark tasks whose issue was closed or whose pull request was merged;
github.activityonly lists open ones. Items gh cannot answer are left
out (#502).- Tasks (タスク): an Issue or PR worked on in a worktree of a source
checkout, or in the checkout itself (in_place). The daemon keeps them
instate/tasks.json(mode 0600; only the daemon writes it, only
uninstall --level purgeremoves it), keyed by the checkout's
canonical path and the number.task.listanswers every task in the
order they were created,watchgainstasks[], andtask.updated
(the wholeTask) andtask.removed {id}follow the changes. Aready
task whose worktree directory is gone or thatgit worktree listno
longer lists becomesmissingontask.list; apreparingtask whose
prepared session ended or never registered becomesfailedwith
error: "interrupted", also after a daemon restart, and a task still
removingwhen the daemon starts isreadywith that error, keeping
itsstage. A changetasks.jsoncannot take is not made: the call
answersinternal. After a daemon restart the bridge's restored watch
sends every task astask.updatedand gone ones astask.removed
(#502). Session.task_id: the daemon ties a session to the worktree task whose
worktree holds itscwd(also live sessions, when the task is
registered), and to anin_placetask only its prepared session and
sessions started with the newsession.spawn {task_id}(#502).- Local IPC for the bridge and
stagent task:task.register(atomic by
the key; afailedtask is prepared again, another number'sin_place
task on the checkout is the new errorin_use),task.progress {id, stage, error?, session_id?}andtask.forget {id}, refused with
agent_refusedto a coding agent's process tree likeconfig.set.
stopon a task alreadyremovingisbusy, so only one removal runs;
stageagentwith an error from the prepared session fails aready
task. Error codesnot_cleanandbusyare reserved fortask.remove
(#502). - Pushes of a session tied to a task are titled
<host> · #N <title>on
ntfy, the generic webhook and chat destinations; a digest of several is
not (#502). task.create(bridge, spawn queue) creates a task and starts its first
session: the daemon registers it ({task, session?, existed}: aready
task starts nothing, apreparingone answers its live prepared
session, afailedone is prepared again), the bridge makes the empty
directory<parent of the checkout>/<checkout name>-<N>and starts the
session there (in the checkout itself forin_place), so itscwdis
the task's place from the start. In it the internalstagent task run <id> -- <command…>fetches, makes the branch (an issue's<N>-<slug>
or the givenbranch, linked withgh issue develop, made locally
frombasewhen gh fails; a PR's head branch withgh pr checkoutin
a detached worktree), adds or adopts the worktree, runsorca.yaml's
scripts.setup(login shell; on Windows a.cmdrunner, or Git for
Windows' bash for a#!script) and then becomes the agent, reporting
each stage withtask.progressand showing everything in the
terminal. A failing stage leaves the taskfailedwithout an agent —
also when the agent itself cannot be started after stageagent;
task.createagain carries on. An emptycommandopens a terminal.
Refused withagent_refusedto a coding agent's process tree (#502).- A task's prepared session reports the harness of the command
stagent task runruns (Session.harness), and a Codex there gets
--no-daemonlike any other (#502). - A worktree task gets its files to copy and shared directories in the
newcopystage ofstagent task run, betweenworktreeandsetup:
orca.yaml'sworktree.sharedDirectoriesof the source checkout that
are gitignored are linked (a symbolic link; on Windows a junction, else
a directory symbolic link; one inside another listed one goes with it),
then the gitignored files the checkout's
.worktreeinclude(Orca's rules: literal paths — a line with*,?
or[is invalid —, at most 1,000, a file
up to 256 KiB) and the app's patterns (config.json
repos.<checkout>.worktree_include, git pathspec globs) select are
copied, up to 2 GiB and 50,000 entries. Tracked, not ignored, missing,
unmatched, invalid and over-the-limit entries and what lies in a linked
directory are skipped with a line in the terminal (tracked ones
silently); a destination that exists is left alone, so a retry copies
only what is missing. A directory that cannot be linked is one warning
line and what is listed under it is copied instead; nothing here fails
the task (#502). task.preview {repo, worktree_include?}(bridge, its own queue,
allowed for agent descendants) answers what a new worktree of the
checkout would get, resolved by the same code:copies(at most 500,
truncated),links,skippedwith the reason (tracked,
not_ignored,missing,no_match,under_link,invalid,
limit),total_bytes, the checkout'sorca.yamlsetupand
orca_error.worktree_includetries patterns that are not saved
(#502).config.jsonrepos.<checkout>.worktree_include, set one checkout at
a time withconfig.set(nullremoves one);config.setrefuses
withbad_requesta key that is not an absolute clean path and a
pattern that is empty, a negation, absolute or has a..or.git
component (#502).task.remove {id, force?, stop_sessions?, skip_archive?, dry_run?}
(bridge, its own queue) removes a task (片付け).dry_runreports the
worktree's uncommitted changes asgit status --porcelainlines
(changes[], ignored files and shared-directory links left out),
the branch's commits on no remote (unpushed) and the task's live
sessions (sessions[]). Changes needforce(not_clean), live
sessionsstop_sessions(busy): they get hangup, then kill after 5 s,
and sessions bound to the task meanwhile are stopped too. Apreparing
task isbusywithoutstop_sessions; with it its sessions are stopped
first (it fails, interrupted) and the removal goes on,in_placetoo.
Thenorca.yaml'sscripts.archiveof the source checkout runs in the
worktree — in a session of the source checkout running the internal
stagent task remove <id>, so its output shows in a terminal — unless
skip_archive; only the shared directories' links are unlinked,git worktree removeruns, and the branch is deleted when git sees it
merged or gh says its pull request was merged at the branch's commit
(a squash merge; a PR task's pull request is looked up by its number,
so a fork's is found too), otherwise kept and named inkept_branch; remote
branches are never deleted. Each stage isTask.stagewhile
removing; a failing one (archive,git worktree remove) takes the
task back with itserror, keeping thatstage(the app offers to skip
the archive when it isarchive), and answersinternal; a second
removal of a task being removed isbusy. The reply comes when
the removal has finished. Amissingtask is forgotten and its
worktree pruned, anin_placeone only forgotten. Refused with
agent_refusedto a coding agent's process tree (#502).task.forgettakeskept_branch?andtask.removedcarries it, so
every watcher learns the branch a removal kept (#502).
Fixed
- A chat message no longer lands on claude's workspace trust prompt
(Accessing workspace:, shown at startup in a folder not trusted yet).
Its options have no numbers, so the holder did not see a menu, and the
message's Enter picked the highlightedNo, exit, ending claude. Now
session.inputrefuses the message withmenu_openwhile the prompt is
up, as on Codex's folder trust prompt; the user answers it in the
terminal, and claude then sends the initial prompt it held. stagent
writes no trust (#502). session.spawnof a program that ends at once no longer fails with
holder exited during startwhen the holder ended with it before the
bridge reached it: the answer is the session the daemon lists. The same
race madetask.createreport a task run that failed at once as a
start failure, andtask.removean archive that failed at once (#502).- Under proot's fake root (
proot -0, an Ubuntu guest on Android), the
chat view and the agent probe find the agents: stagent took its
own uid fromgeteuid, which proot fakes, and every process's from
/proc/<pid>/status, which proot leaves at the kernel's uid, so every
process counted as another user's. On Linux stagent now reads its own
uids from/proc/self/statustoo; elsewhere the uids agree and nothing
changes (#529).
stagent 0.10.0 — server binary for SSH Term. Built from this tag (abda34b) with go1.27.1 via scripts/release.sh (reproducible: CGO_ENABLED=0, -trimpath, no VCS stamp, empty build id); verify downloads against SHA256SUMS.