Skip to content

feat(cli): add background process monitor and session cron scheduling - #14312

Merged
iscekic merged 1 commit into
mainfrom
kwf/req-20260918-153037-33df
Sep 22, 2026
Merged

iscekic merged 1 commit into
mainfrom
kwf/req-20260918-153037-33df

Conversation

@iscekic

@iscekic iscekic commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

Changelog for users

  • background_process gains a monitor action that returns a command's output as it arrives, so a waiting agent reads progress instead of polling logs.
  • monitor stops when the process exits, and reaching a cap leaves the process running and names its id for logs, status, or stop.
  • The line cap defaults to 200 lines (1–1000) and the wall-time cap to 120000 ms (5000–600000).
  • cron_create schedules a recurring task from a 5-field expression, or a one-shot from an absolute time or relative delay.
  • cron_list shows each task's id, schedule, next fire time, and prompt; cron_delete cancels a task by id.
  • A session holds at most 10 tasks; each task expires seven days after creation, and a schedule whose next fire falls past that expiry is rejected.
  • A due task fires between turns after the session is idle; missed fires are not replayed and fire times carry deterministic jitter.
  • Cron tasks survive --resume; the existing start, list, status, logs, stop, and restart actions keep working unchanged.

Changelog for maintainers

  • packages/opencode/src/kilocode/wakeup/index.ts:344 — Accepted: cron_create now rejects a schedule whose first fire lands past the seven-day expiry instead of arming and retaining it. Unchanged: a next window past expiresAt still drops the task, records persist under cron/<sessionID>/<id>.json, adopt re-arms them, cancelSession deletes them, and they never hold Keep Awake (10 per session, 7-day TTL).
  • packages/opencode/src/kilocode/tool/background-process.ts:195 — Accepted: non-persistent processes now finalize on close after stdio drains, so monitor keeps trailing output and the exit path cannot drop it. Unchanged: the 200 ms poll, streamed output through ctx.metadata, the ctx.abort race, the sandbox block on start/monitor/restart, and the inherit/persistent rejection.
  • packages/opencode/src/kilocode/wakeup/index.ts:172 — Accepted: a fire outlasting its interval now re-arms the next window rather than dropping it, so the schedule cannot stall. Fire.run keeps its kind option (wakeup | cron) and the [scheduled cron task] label, and the next window is still persisted before the resume runs — review this fire/re-arm path first for crash safety and missed-window handling.
  • packages/opencode/src/kilocode/wakeup/cron.ts:135 — Accepted: the horizon is eight years and whole non-matching days are skipped, so Feb-29 schedules resolve without the linear minute scan. The engine stays a pure validate/next/jitter/isExpression module re-exported by the wakeup schema; Wakeup.Service keeps cronCreate/cronList/cronCancel with CronInfo/CronInput and the InvalidSchedule/TooManyCron errors.
  • packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts:147 — Accepted: the test asserts the due time falls inside the jitter window instead of recomputing next()/jitter() under test.
  • packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts:99 — Accepted: new tests set the reenter hook, covering the in-flight fire guard and an overdue fire that must not stall.
  • packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts:411 — Accepted: the adoption test pins a near due time, asserts adopt does not fire early, then waits for the fire.
  • .changeset/ — Accepted: added a minor changeset describing the monitor action and the session cron tools for release notes. KiloToolRegistry still wires the three cron tools in the node graph and exposes them only to the cli/vscode clients.

E2E proof — log excerpts

  • Post-repair checks (2026-09-18): seven targeted checks passed — the wakeup cron and service suites, the background-process monitor and tool suites, the schedule-wakeup/cancel-wakeup tool suites, tsgo typecheck, and oxlint on the changed files; one background-process shutdown check passed on retry after a flake.
  • The full CLI test:ci run (274 test files) hit the 2400 s gate wall and is recorded as advisory, not a regression; it is not counted as a pass.
  • TUI screenshot capture failed (missing pyte/Pillow), so no screen captures exist and all proof is logs.
  • The appended [e1]–[e5] excerpts were collected 2026-09-18 during the original feature pass, before this repair.
  • Superseded: [e1] filled the cap with a non-firing schedule chosen to avoid mid-run fires; the repaired create path now rejects schedules whose first fire falls past the seven-day expiry, so that transcript no longer proves the current create code — the wakeup suites cover the cap and the new rejection.
  • Superseded: [e4] predates the finalize-on-close fix, so its failure transcript no longer proves the repaired stop path — the background-process monitor suite covers it.
  • Still valid: [e2], [e3], and [e5] prove behaviors the repair did not change — the line and wall-time caps on idling processes and the existing background_process actions; they were not re-collected.
  • No live session scenarios were re-run after the repair; the repaired paths are proved by the targeted suites above.
Owner request

Add two agent tools to the Kilo CLI (the kilocode repo), modelled on Claude Code 2.1.272 but shaped
for what we already have.

(1) Monitor — a streaming mode for the existing background_process tool. Today it offers
start / list / status / logs / stop, so a long command (xcodebuild, gradle, vitest, a deploy)
has to be polled. Add a mode that runs a command and feeds each output line back to the model as it
arrives, bounded by a line cap and a wall-time cap, so a waiting agent reads progress instead of
polling. The existing non-streaming actions must keep working unchanged.

(2) Cron — a recurrence primitive on the existing wakeup service
(packages/opencode/src/kilocode/wakeup, the service behind schedule_wakeup / cancel_wakeup).
Add cron_create, cron_list and cron_delete, or an every: parameter on schedule_wakeup if
that is the smaller change. Copy the semantics of Claude Code's session cron:

  • standard 5-field cron expression, plus a one-shot form (fire once at a future time);
  • a short id per task, and a per-session cap;
  • a documented minimum interval;
  • a due task fires between turns: it waits for idle, and missed fires are not replayed one-for-one;
  • deterministic jitter on the fire time, so many sessions do not fire at once;
  • self-expiry so a forgotten loop cannot run forever (Claude Code uses seven days);
  • it survives --resume, like the existing wakeups;
  • cron_list shows every scheduled task with its schedule, next fire time and prompt.

Why. The only recurrence today is one-shot chaining — a series of schedule_wakeup calls, capped
at 10 pending per session — plus systemd timers outside the agent (the nightly improver, the fleet
reap, the janitors, the pr-loop). Moving recurrence into the agent deletes that scaffolding and makes
periodic work a first-class capability. The two features pair: a cron job whose output streams back
needs no polling at all.

Acceptance.

  • A streaming background command returns output lines incrementally, with a documented cap.
  • cron_create accepts a 5-field expression and a one-shot form; cron_list lists them;
    cron_delete cancels by id.
  • A cron task fires between turns, waits for idle, expires on schedule, and survives --resume.
  • Tests for each, and the tool documentation updated.

E2E proof — log excerpts

[e1] Live: cron cap (retryable) -> pass :: 11th create returns 'Too many cron tasks ... Cancel one with cron_delete', then delete ('Deleted cron task wku_0b5f204e0001BlCJKdTA0QArUi'), retry create succeeds and cron_list holds 10 (non-firing schedule used to avoid mid-run fires).
[e2] Live: monitor line cap -> pass :: Under SHELL=/bin/sh the monitor returns reason 'lines', lines 200 (line301..line500) with trailer '[monitor stopped: reached the 200-line cap; the process is still running]', list still shows the running id, and stop terminates it.
[e3] Live: monitor wall-time cap -> pass :: monitor sleep 60 with timeout 5000 returns in 5195 ms with reason 'time' and the '5000 ms wall-time cap' trailer, list shows the pid running, and stop returns status stopped.
[e4] Live: monitor failure and empty list -> pass :: Monitoring a non-zero exit yields 'before' plus '[monitor stopped: process failed]' with no crash, and a fresh session's list returns 'No background processes are available for this session.'
[e5] Live: existing background_process actions unchanged -> pass :: start returns a running id ('Started: sleep 300'), list and status report it running, logs returns '(no output)', and stop returns status stopped.

E2E proof — log excerpts

[e1] Live: cron cap (retryable) -> pass :: 11th create returns 'Too many cron tasks ... Cancel one with cron_delete', then delete ('Deleted cron task wku_0b5f204e0001BlCJKdTA0QArUi'), retry create succeeds and cron_list holds 10 (non-firing schedule used to avoid mid-run fires).
[e2] Live: monitor line cap -> pass :: Under SHELL=/bin/sh the monitor returns reason 'lines', lines 200 (line301..line500) with trailer '[monitor stopped: reached the 200-line cap; the process is still running]', list still shows the running id, and stop terminates it.
[e3] Live: monitor wall-time cap -> pass :: monitor sleep 60 with timeout 5000 returns in 5195 ms with reason 'time' and the '5000 ms wall-time cap' trailer, list shows the pid running, and stop returns status stopped.
[e4] Live: monitor failure and empty list -> pass :: Monitoring a non-zero exit yields 'before' plus '[monitor stopped: process failed]' with no crash, and a fresh session's list returns 'No background processes are available for this session.'
[e5] Live: existing background_process actions unchanged -> pass :: start returns a running id ('Started: sleep 300'), list and status report it running, logs returns '(no output)', and stop returns status stopped.

@iscekic
iscekic marked this pull request as draft September 18, 2026 20:05
Comment thread packages/opencode/src/kilocode/wakeup/index.ts
Comment thread packages/opencode/src/kilocode/tool/background-process.ts
Comment thread packages/opencode/src/kilocode/wakeup/index.ts Outdated
Comment thread packages/opencode/src/kilocode/wakeup/cron.ts Outdated
Comment thread packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts Outdated
Comment thread packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts
Comment thread packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts Outdated
@kilo-code-bot

kilo-code-bot Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (1 file)
  • packages/opencode/test/kilocode/background-process-monitor.test.ts
Previous Review Summaries (3 snapshots, latest commit 840c968)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 840c968)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/opencode/test/kilocode/background-process-monitor.test.ts 140 The new descendant test backgrounds a pipe-holding grandchild that stopSession cannot reap, so the process (and its inherited pipe handles) outlives the test.
Files Reviewed (4 files)
  • packages/opencode/src/kilocode/background-process/index.ts
  • packages/opencode/src/kilocode/wakeup/index.ts
  • packages/opencode/test/kilocode/background-process-monitor.test.ts - 1 issue
  • packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts

Fix these issues in Kilo Cloud

Previous review (commit 8bca44e)

Status: 4 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 3
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/kilocode/background-process/index.ts 991 Non-persistent processes now finalize only on close; a descendant holding the stdio pipes can keep the process non-terminal, so status/monitor never observe the exit.

SUGGESTION

File Line Issue
packages/opencode/src/kilocode/wakeup/index.ts 343 The TTL rejection compares the jittered dueAt, so a boundary schedule is accepted or rejected depending on the id's jitter.
packages/opencode/src/kilocode/wakeup/index.ts 426 The new adopt drop branch has no test; the existing expiry test exercises the rearm drop instead.
packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts 354 Real setTimeout wait inside a TestClock test is the repo's documented flake anti-pattern.
Files Reviewed (11 files)
  • .changeset/background-monitor-and-session-cron.md
  • packages/opencode/src/kilocode/background-process/index.ts - 1 issue
  • packages/opencode/src/kilocode/tool/cancel-wakeup.ts
  • packages/opencode/src/kilocode/tool/cron-create.txt
  • packages/opencode/src/kilocode/tool/cron.ts
  • packages/opencode/src/kilocode/tool/schedule-wakeup.ts
  • packages/opencode/src/kilocode/tool/wakeup-format.ts
  • packages/opencode/src/kilocode/wakeup/cron.ts
  • packages/opencode/src/kilocode/wakeup/index.ts - 2 issues
  • packages/opencode/test/kilocode/wakeup/cron.test.ts
  • packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts - 1 issue

Fix these issues in Kilo Cloud

Previous review (commit 613f97b)

Status: 8 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 6
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/kilocode/wakeup/index.ts 344 7-day cron TTL is not enforced when a task's first/next dueAt is already beyond expiresAt; the task is armed and retained past expiry, holding a session slot.
packages/opencode/src/kilocode/tool/background-process.ts 195 monitor stops on terminal status, which is set on proc.once("exit") before stdout/stderr drain, so trailing output can be lost and the exit test can flake.

SUGGESTION

File Line Issue
packages/opencode/src/kilocode/wakeup/index.ts 172 cronFiring early return can silently drop a re-arm if a fire outlasts the interval, stalling the schedule.
packages/opencode/src/kilocode/wakeup/cron.ts 135 366-day horizon rejects valid Feb-29 expressions and reports them as invalid schedules.
packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts 147 Expected dueAt is recomputed with next()/jitter() under test, duplicating implementation logic.
packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts 99 The reenter hook is never set, so the in-flight fire guard has no test coverage.
packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts 411 expect(calls).toEqual([]) is vacuous because the task is minutes from firing.
.changeset/ — User-facing feature has no changeset file (summary only; add one so the release notes describe the monitor and cron tools).
Files Reviewed (17 files)
  • packages/opencode/src/kilocode/tool/background-process.ts - 1 issue
  • packages/opencode/src/kilocode/tool/background-process.txt
  • packages/opencode/src/kilocode/tool/cron-create.txt
  • packages/opencode/src/kilocode/tool/cron-delete.txt
  • packages/opencode/src/kilocode/tool/cron-list.txt
  • packages/opencode/src/kilocode/tool/cron.ts
  • packages/opencode/src/kilocode/tool/registry.ts
  • packages/opencode/src/kilocode/wakeup/cron.ts - 1 issue
  • packages/opencode/src/kilocode/wakeup/index.ts - 2 issues
  • packages/opencode/src/kilocode/wakeup/resume.ts
  • packages/opencode/src/kilocode/wakeup/schema.ts
  • packages/opencode/test/kilocode/background-process-monitor.test.ts
  • packages/opencode/test/kilocode/background-process-tool.test.ts
  • packages/opencode/test/kilocode/tool-registry-indexing.test.ts
  • packages/opencode/test/kilocode/tool/cron.test.ts
  • packages/opencode/test/kilocode/wakeup/cron.test.ts
  • packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts - 3 issues

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

@iscekic
iscekic force-pushed the kwf/req-20260918-153037-33df branch 2 times, most recently from c566b5c to 8bca44e Compare September 18, 2026 23:46
@iscekic
iscekic marked this pull request as ready for review September 18, 2026 23:57
Comment thread packages/opencode/src/kilocode/background-process/index.ts
Comment thread packages/opencode/src/kilocode/wakeup/index.ts Outdated
Comment thread packages/opencode/src/kilocode/wakeup/index.ts
Comment thread packages/opencode/test/kilocode/wakeup/wakeup-cron.test.ts Outdated
@iscekic
iscekic marked this pull request as draft September 19, 2026 00:08
@iscekic
iscekic force-pushed the kwf/req-20260918-153037-33df branch from 8bca44e to 540b172 Compare September 19, 2026 00:58
@iscekic
iscekic force-pushed the kwf/req-20260918-153037-33df branch from 540b172 to 840c968 Compare September 19, 2026 01:40
@iscekic
iscekic marked this pull request as ready for review September 19, 2026 02:00
Comment thread packages/opencode/test/kilocode/background-process-monitor.test.ts Outdated
@iscekic
iscekic marked this pull request as draft September 19, 2026 02:06
@iscekic
iscekic force-pushed the kwf/req-20260918-153037-33df branch from 840c968 to c7a4354 Compare September 19, 2026 02:41
@iscekic
iscekic force-pushed the kwf/req-20260918-153037-33df branch from c7a4354 to 5fe6f23 Compare September 19, 2026 02:57
@iscekic
iscekic force-pushed the kwf/req-20260918-153037-33df branch from 5fe6f23 to d739afe Compare September 19, 2026 04:48
@iscekic
iscekic marked this pull request as ready for review September 19, 2026 05:04
@iscekic iscekic added the human-ready PR is ready for a human review. label Sep 19, 2026
@iscekic iscekic self-assigned this Sep 19, 2026
@iscekic
iscekic merged commit dfeeb48 into main Sep 22, 2026
32 checks passed
@iscekic
iscekic deleted the kwf/req-20260918-153037-33df branch September 22, 2026 12:07
iscekic added a commit that referenced this pull request Sep 23, 2026
# Make `/goal` and the timing tools one system

Surface: cli. Verified against origin/main at 516e9ca, which contains PR #14312 (commit dfeeb48, "feat(cli): add background process monitor and session cron scheduling"); state that revision in the PR body.

A goal must be able to wait on time, on a scheduled task, or on a background process and resume itself, and no scheduling tool may spin the goal loop. Today `/goal` and the timing tools are two subsystems that do not know each other exists, and the combination that most needs to work is broken. This item is the fix, plus the done-criteria that prove it.

## The timing surface

Five tools plus one monitor, all one scheduler:

- `schedule_wakeup` — `packages/opencode/src/kilocode/tool/schedule-wakeup.ts`, with the model-facing description in `tool/schedule-wakeup.txt`. One shot.
- `cancel_wakeup` — `packages/opencode/src/kilocode/tool/cancel-wakeup.ts`, with `list` and `cancel` (`tool/cancel-wakeup.txt`).
- `cron_create` — `packages/opencode/src/kilocode/tool/cron.ts` (`tool/cron-create.txt`). Takes exactly one of `cron` (a 5-field expression, smallest interval one minute), `when`, or `delay`, so it covers both recurring and one-shot.
- `cron_list` — `packages/opencode/src/kilocode/tool/cron.ts` (`tool/cron-list.txt`).
- `cron_delete` — `packages/opencode/src/kilocode/tool/cron.ts` (`tool/cron-delete.txt`).
- `background_process` — the monitor added by the same PR, `packages/opencode/src/kilocode/tool/background-process.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human-ready PR is ready for a human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants