Feature request: make long unattended runs possible (continuation currently lives in the client)
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 19.9k
- Forks
- 2.3k
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 30
Description
I want to hand jcode an objective, walk away, and come back hours later to real progress. /overnight is clearly built for exactly this, so I went looking for what limits how long a run can actually last. Notes below from v0.64.2 (438fc31f), roughly in order of how much each one binds.
1. A run is bounded by the terminal staying open
Session state and agent execution are server-side, but every decision to continue is client-side. There is no continuation logic anywhere under crates/jcode-app-core/src/server/.
The practical effect is that closing the terminal does not crash a run. The session survives on the server, fully intact, and never advances again, because nothing server-side will poke it.
/overnight is subject to this because its driver, overnight_auto_poke, is App state (tui/app.rs:645). That is also why the run must be visible:
$ rg -n 'start_overnight_run|use_current_session' crates/ src/ --glob '!**/overnight.rs'
crates/jcode-tui/src/tui/app/commands_overnight.rs:43: use_current_session: true,
crates/jcode-tui/src/tui/app/commands_overnight.rs:45: match crate::overnight::start_overnight_run(options) {
One call site, hardcoded, so the else branch at app-core/src/overnight.rs:151 never runs. spawn_supervisor, run_supervisor, run_turn_monitored, and five of the six prompt builders in overnight-core/src/prompts.rs are unreachable. This looks like it dates to 509f0aec1 ("Make overnight run visible in current session"), and visible mode is clearly the better UX, so I assume the background path was staged rather than dropped. If there is a reachable headless entry point I missed, most of this issue goes away.
The same split shows up as a reliability problem even with a client attached. The comment at tui/app/remote.rs:1497 describes a queued follow-up sitting undispatched while the spinner spins and the model is idle, invisible to detect_and_cancel_stall, needing a dedicated starvation watchdog to recover.
Anything that calls Agent::run_once_capture directly gets no continuation at all: swarm workers (server/swarm.rs:1584), the swarm planner and integrator (:1638, :1688), and jcode run.
2. A run does not survive its own process
OvernightManifest.process_id is recorded (overnight.rs:117) and never read. Kill the process and the manifest stays Running forever, so /overnight status reports a live run and /overnight cancel cancels a corpse. Nothing scans for orphaned Running manifests at startup, so a crash or a self-update ends a run silently.
overnight.rs:306 also propagates with ?, so a single rate limit or transient provider error would end an eight-hour run if that path were reachable.
3. Nothing acts on usage during a run
gather_preflight projects provider usage once at launch. Resource snapshots every five minutes (RESOURCE_SAMPLE_INTERVAL) are written as events that gate nothing. For a genuinely long run on a subscription plan I would expect quota, not the terminal, to be the real ceiling once (1) is fixed.
ambient/scheduler.rs::AdaptiveScheduler already implements the right policy here (rolling usage window, should_pause, on_rate_limit_hit). Overnight has its own one-shot projection instead.
4. Longer only helps if the stopping predicate is about progress
This is the part I would most want your view on, because it decides whether the rest is worth doing.
overnight_auto_poke is the best continuation logic in the tree. It fingerprints event count, task-card counts by status, validated count, session message count, review-notes mtime, and validation file count; stops after OVERNIGHT_STALL_LIMIT = 3 no-progress turns; and budgets 4 pokes per hour clamped to [4, 48].
But the fingerprint measures activity, not progress toward the objective. An agent that keeps writing task cards and appending events satisfies "is it still working" indefinitely. At 4 pokes per hour, an 8 hour run spends 32 of its 48 budget, so the budget is not usually what stops it either.
Meanwhile Initiative (base/src/goal.rs, jcode-task-types) already models the thing a predicate would want: success_criteria, milestones, next steps, blockers, persisted per project and global, and synced into the memory graph. Nothing reads it automatically. No attach at session start, no injection into a turn, and the criteria are never evaluated. base/src/prompt/mission_continuation.md reads like it was written for exactly this and is unreachable since 793759e7c disabled /mission (mission::set has no callers, so active_system_reminder always returns None).
What I am asking for
Ordered by how much each one actually extends a useful run:
- Continuation as a harness primitive in
jcode-app-core, owning the injection and a per-session budget, with the existing checks registered as predicates against it. Headless overnight then mostly falls out of deleting theuse_current_sessionfork, and swarm andjcode runinherit continuation for free. - Run survival: liveness check on
process_id, a startup sweep for orphanedRunningmanifests, and tolerating N consecutive turn failures instead of ending on the first. - Usage-aware degrade for long runs, reusing
AdaptiveSchedulerrather than the one-shot projection. - A progress predicate grounded in initiative success criteria rather than activity, so that "longer" translates into "further".
(1) and (4) are the pair that matter. (1) alone raises the ceiling from "as long as my terminal is open" to "as long as the machine is up", which is worth having on its own but mostly buys more of whatever the agent was already doing.
Questions
- Should the shared poke budget be per session, per objective, or per run? Overnight scales it by duration, which does not generalize to an interactive session.
- Is the fingerprint deliberately specific to task cards, or worth generalizing?
- Is unattended-without-a-client a direction you want at all? Ambient already has the adjacent half (
AdaptiveScheduler, the persistent wake queue,schedulewithtarget: resume | spawn), so a headless overnight would have somewhere to live rather than needing a new daemon. But if visible-only is a deliberate product stance, that changes the answer to all of the above.
Happy to prototype (1) as a focused change if the direction is useful, treated as a reference per CONTRIBUTING rather than something to merge. Two constraints any implementation has to respect: overnight.rs sits at exactly its size-ratchet baseline (1275/1275) and turn_loops.rs is 14 lines from becoming newly tracked, so this has to land in new modules; and injected content has to stay out of the cached system prefix or it trips KvCacheMissReason::HarnessSystemChanged.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with crates/jcode-tui/src/tui/app/commands_overnight.rs and app-core/src/overnight.rs, then trace the continuation and failure paths named in the issue. Compare those paths with ambient/scheduler.rs::AdaptiveScheduler and base/src/goal.rs::Initiative. Done should be defined after resolving the headless-run and shared-budget questions, with tests covering the chosen continuation and recovery behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- ai, cli, devtools
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100