Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(cli): add scheduled wakeup and cancel tools - #14094

Draft
iscekic wants to merge 1 commit into
mainfrom
kwf/agent-scheduled-wakeup-tool-c6b8
Draft

feat(cli): add scheduled wakeup and cancel tools#14094
iscekic wants to merge 1 commit into
mainfrom
kwf/agent-scheduled-wakeup-tool-c6b8

Conversation

@iscekic

@iscekic iscekic commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Changelog for users

  • The agent can schedule a future wakeup for its own session, and the session resumes at that time with the prompt it set.
  • The agent can list the wakeups pending for its session and cancel one by id.
  • Scheduled wakeups survive the process: they are stored and re-armed when the project is opened again, and overdue wakeups fire on start.
  • A wakeup fires no sooner than 10 seconds and no later than 7 days out; a session can hold at most 10 pending wakeups, and a past time is rejected.
  • On wake, the model receives its scheduled prompt marked [scheduled wakeup], with a note that no user is present.

Changelog for maintainers

  • Time input: exactly one of when (ISO-8601; an explicit offset is absolute, otherwise the host timezone applies) or delay (30s, 5m, 2h, 1d; a bare number is seconds). A past time is rejected, a delay under 10 seconds clamps up, a time past 7 days clamps down, and a session holds at most 10 pending wakeups.
  • Storage and keying: each wakeup persists through the existing file-backed Storage service under wakeup/sessionID/id, and carries sessionID and directory so a fire resumes the right session in the right project. The guarantee reaches a running process and the next CLI start in that directory, not a separate daemon.
  • Re-arm on start: bootstrap calls adopt(directory) for the instance, which fires overdue wakeups immediately and arms timers for the rest.
  • Wake context: resume sends a synthetic text part beginning [scheduled wakeup], the scheduled prompt, and a note that no user is present with the wake id and due time.
  • Resume and failure: an idle session is resumed through the session prompt path rather than kept alive; if the session or its directory is gone, resume throws and logs an error-level wakeup could not resume session naming the id, session, and directory.
  • Concurrency: each wakeup owns one timer fiber; two due at the same instant both fire, and a fire while the model is running forks a detached prompt instead of blocking the timer.
  • Registration and release: both tools are exposed to CLI and VS Code clients, the wakeup service is wired into the app runtime, bootstrap, and the tool-registry node graph, and a changeset marks the CLI release as minor.
  • Review hints and guards: check the resume path first, then the clamp math. The new resume integration test has an allowlist entry in the promise-facade guard because a static layer dependency between the wakeup node and the session prompt node is impossible. An orphaned ghostty-web patch with no patchedDependencies entry was removed.

E2E proof — log excerpts

[e1] Live CLI happy path plus list/cancel/empty/duplicate-cancel -> pass :: Tool result 'Scheduled wakeup wku_..., due 2026-09-13T18:11:15.950Z (in 15s). ... resumes with: WAKE_OK_123'; the wake fired as a user turn '[scheduled wakeup] WAKE_OK_123'; second wake listed, 'Cancelled wakeup wku_... (2026-09-13T18:11:51.584Z).', duplicate cancel 'No pending wakeup with id wku_...', 0 WAKE_CANCEL_456 banners after its due time, final 'No pending wakeups for this session.'.
[e2] [live cli] Happy + wake context -> pass :: Typed `run` gave 'Scheduled wakeup wku_..., due 2026-09-13T18:13:11.845Z (in 15s). When it fires this session resumes with: poll the deploy status'; past the due time the session resumed with user turn '[scheduled wakeup] poll the deploy status' and the assistant continued (one-shot run exits first, so the resume was observed at the next start in that directory).
[e3] [live cli] List then cancel -> pass :: 'wku_... due 2026-09-13T18:10:34.716Z (in 20s) WAKE_CANCEL_456', then 'Cancelled wakeup wku_... (2026-09-13T18:10:34.716Z).'; 0 wake banners after the due time; re-list 'No pending wakeups for this session.'.
[e4] [live cli] Clamps -> pass :: delay "1s" -> 'due 2026-09-13T18:09:41.474Z (in 10s). Requested delay: "1s" is under the 10-second minimum and was raised to it.'; when 10 days out -> 'due 2026-09-20T18:09:31.772Z (in 7d). Requested when: "..." is beyond the 7-day horizon and was pulled back to it.'.
[e5] [live cli] Edge count / non-retryable unhappy -> pass :: 10 schedules completed; the 11th returned 'Too many scheduled wakeups: this session already holds the maximum of 10 pending wakeups. Cancel one with cancel_wakeup before scheduling another.'; the following list showed exactly the same 10.
[p1] Live CLI validation states -> pass :: Re-run because the diff changes this path: past `when` -> 'Invalid wakeup input :: Wakeup time is not in the future: ...'; delay 3s -> 'in 10s' + minimum notice; when 30d -> 'in 7d' + horizon notice; fresh-session count: 10 scheduled, 11th rejected with the summary-led cap line, list rows 10.
/home/igor_kilocode_ai/.local/share/kwf/sections/agent-scheduled-wakeup-tool-c6b8/e2e-cli/e1-flow.log
---
[schedule_wakeup completed]
Scheduled wakeup wku_09bf77781001t59WEX4QxwKrCb, due 2026-09-13T18:11:51.584Z (in 30s).
When it fires this session resumes with: WAKE_CANCEL_456
---
[cancel_wakeup completed]
wku_09bf77781001t59WEX4QxwKrCb  due 2026-09-13T18:11:51.584Z (in 29s)  WAKE_CANCEL_456
---
[cancel_wakeup completed]
Cancelled wakeup wku_09bf77781001t59WEX4QxwKrCb (2026-09-13T18:11:51.584Z).
---
[cancel_wakeup completed]
No pending wakeup with id wku_09bf77781001t59WEX4QxwKrCb.
---
[cancel_wakeup completed]
No pending wakeups for this session.
---
=== the scheduled-wakeup user turn ===
[scheduled wakeup] WAKE_OK_123
(No user is present. You scheduled this wakeup yourself as wku_09bf726e4001goagkmuVbKGF0B, due 2026-09-13T18:11:15.950Z.)
=== WAKE_OK_123 wake banner count (expect 1) ===
1
=== WAKE_CANCEL_456 wake banner count after its due time (expect 0) ===
0
/home/igor_kilocode_ai/.local/share/kwf/sections/agent-scheduled-wakeup-tool-c6b8/e2e-cli/e2-flow.log
e2 live cli happy + wake context (typed CLI command)
command: bun run --conditions=browser <wt>/packages/opencode/src/index.ts run "Use schedule_wakeup with delay 15s and prompt 'poll the deploy status', then end your turn." -m mock/mock-model --format 
cwd /tmp/kwfv/cli-proj  KILO_CONFIG_DIR=/tmp/kwfv/config2  XDG_DATA_HOME=/tmp/kwfv/data2  mock http://127.0.0.1:40247
session ses_f640746f8ffetZ1O3fI8mVVLi5
=== tool result from the typed CLI run (e2-run.jsonl) ===
"output":"Scheduled wakeup wku_09bf8eba6001hXYRDR8BWirc2y, due 2026-09-13T18:13:11.845Z (in 15s).
When it fires this session resumes with: poll the deploy status"
=== the same session after its wake was adopted and resumed (e2-msgs.json) ===
--- user turns ---
"Use schedule_wakeup with delay 15s and prompt 'poll the deploy status', then end your turn."
[scheduled wakeup] poll the deploy status
(No user is present. You scheduled this wakeup yourself as wku_09bf8eba6001hXYRDR8BWirc2y, due 2026-09-13T18:13:11.845Z.)
--- assistant turns ---
scheduled, ending my turn
acknowledged scheduled wakeup
=== persisted wake file after adoption (expect empty) ===
total 8
drwxrwxr-x 2 igor_kilocode_ai igor_kilocode_ai 4096 Sep 13 18:13 .
drwxrwxr-x 3 igor_kilocode_ai igor_kilocode_ai 4096 Sep 13 18:12 ..
/home/igor_kilocode_ai/.local/share/kwf/sections/agent-scheduled-wakeup-tool-c6b8/e2e-cli/e3-flow.log
e3 live CLI list then cancel: pending shown, cancel removes it, it never fires, list then empty
server http://127.0.0.1:38801  mock http://127.0.0.1:40247  session ses_f6409a0b2ffe3jKtFFfK312Q7u
=== tool results (state output) ===
[schedule_wakeup completed]
Scheduled wakeup wku_09bf6724d001p447sdLVGiidCV, due 2026-09-13T18:10:34.716Z (in 20s).
When it fires this session resumes with: WAKE_CANCEL_456
---
[cancel_wakeup completed]
wku_09bf6724d001p447sdLVGiidCV  due 2026-09-13T18:10:34.716Z (in 20s)  WAKE_CANCEL_456
---
[cancel_wakeup completed]
Cancelled wakeup wku_09bf6724d001p447sdLVGiidCV (2026-09-13T18:10:34.716Z).
---
[cancel_wakeup completed]
No pending wakeups for this session.
---
=== wake banner count for WAKE_CANCEL_456 (expect 0) ===
0
/home/igor_kilocode_ai/.local/share/kwf/sections/agent-scheduled-wakeup-tool-c6b8/e2e-cli/e4-flow.log
e4 live CLI clamps: delay 1s -> ~10s minimum with clamp notice; when 10 days -> ~7 days horizon with clamp notice
server http://127.0.0.1:38801  mock http://127.0.0.1:40247  session SID=ses_f640a4efaffeYecCM6ITBu6hRB
input: delay "1s" (expect minimum clamp) and when = now+10d (expect horizon clamp)
=== tool results (state output) ===
[schedule_wakeup completed]
Scheduled wakeup wku_09bf5c970001BSni46HgdopFUR, due 2026-09-13T18:09:41.474Z (in 10s). Requested delay: "1s" is under the 10-second minimum and was raised to it.
When it fires this session resumes with: MIN_1S
---
[schedule_wakeup completed]
Scheduled wakeup wku_09bf5ca96001pT42a0xG96N74w, due 2026-09-20T18:09:31.772Z (in 7d). Requested when: "2026-09-23T18:09:31.760Z" is beyond the 7-day horizon and was pulled back to it.
When it fires this session resumes with: HORIZON_10D
---
=== assistant text ===
Schedule with delay 1s and with a when 10 days out.
clamps done
/home/igor_kilocode_ai/.local/share/kwf/sections/agent-scheduled-wakeup-tool-c6b8/e2e-cli/e5-flow.log
When it fires this session resumes with: W9
---
[schedule_wakeup completed]
Scheduled wakeup wku_09bf62b1b001QAQsyOxYqIxGBR, due 2026-09-13T19:09:56.490Z (in 60m).
When it fires this session resumes with: W10
---
[schedule_wakeup completed]
Too many scheduled wakeups: this session already holds the maximum of 10 pending wakeups. Cancel one with cancel_wakeup before scheduling another.
---
[cancel_wakeup completed]
wku_09bf6219c001jlEGKZD7Xr9H4n  due 2026-09-13T19:09:54.056Z (in 60m)  W1
wku_09bf62294001dWTONUh5Zp6IIl  due 2026-09-13T19:09:54.303Z (in 60m)  W2
wku_09bf623c500153a4RruKm6j27T  due 2026-09-13T19:09:54.606Z (in 60m)  W3
wku_09bf62519001x3x7jN4LGB91IK  due 2026-09-13T19:09:54.936Z (in 60m)  W4
wku_09bf62643001pwuplaEHdSc916  due 2026-09-13T19:09:55.244Z (in 60m)  W5
wku_09bf62741001fEJ5sudPURw5O0  due 2026-09-13T19:09:55.503Z (in 60m)  W6
wku_09bf6285e001cDMutwBXnfyWLd  due 2026-09-13T19:09:55.783Z (in 60m)  W7
wku_09bf6296e001154BDR5wWwzIaJ  due 2026-09-13T19:09:56.062Z (in 60m)  W8
wku_09bf62a22001AxooRWPtybI0MB  due 2026-09-13T19:09:56.242Z (in 60m)  W9
wku_09bf62b1b001QAQsyOxYqIxGBR  due 2026-09-13T19:09:56.490Z (in 60m)  W10
---
=== schedule_wakeup results count and rejection ===
scheduled ids: 10
rejected: 1
/home/igor_kilocode_ai/.local/share/kwf/sections/agent-scheduled-wakeup-tool-c6b8/e2e-cli/p1-flow.log
p1 live CLI validation states (re-run: diff changed the clamp echo and the cap message)
server http://127.0.0.1:38801  mock http://127.0.0.1:40247
session a/b/c ses_f640624e1ffeJEf1XJdbaXymBY   session count ses_f6405ede0ffeEALl2CG2vNcusU
=== (a/b/c) title :: first output line ===
Invalid wakeup input :: Wakeup time is not in the future: 2026-09-13T18:12:58.652Z
Scheduled wakeup wku_09bf9de1f001GUUXAF15dZHlXf :: Scheduled wakeup wku_09bf9de1f001GUUXAF15dZHlXf, due 2026-09-13T18:14:08.925Z (in 10s). Requested delay: "3s" is under the 10-second minimum and was 
Scheduled wakeup wku_09bf9df2d001MkFOkewvjimob1 :: Scheduled wakeup wku_09bf9df2d001MkFOkewvjimob1, due 2026-09-20T18:13:59.190Z (in 7d). Requested when: "2026-10-13T18:13:59.155Z" is beyond the 7-day
=== count part: scheduled / rejected / list rows ===
scheduled: 10
rejected:  1
rejection line:
Too many scheduled wakeups: this session already holds the maximum of 10 pending wakeups. Cancel one with cancel_wakeup before scheduling another.
list rows: 10
list output:
wku_09bfa1497001oEHB0jFZmdTwhm  due 2026-09-15T18:14:12.869Z (in 2d)  W1
wku_09bfa15e1001414YNoP04W2JbI  due 2026-09-15T18:14:13.197Z (in 2d)  W2
wku_09bfa1767001ccu6x1Jauvi6hU  due 2026-09-15T18:14:13.540Z (in 2d)  W3
wku_09bfa1846001kKItLbssbS7kx0  due 2026-09-15T18:14:13.815Z (in 2d)  W4
wku_09bfa1907001dlPDPiuobUVlbm  due 2026-09-15T18:14:14.006Z (in 2d)  W5
wku_09bfa19e7001wfw9HCspWxjpMb  due 2026-09-15T18:14:14.230Z (in 2d)  W6
wku_09bfa1b050012ct5aSsg6MxhUP  due 2026-09-15T18:14:14.514Z (in 2d)  W7
wku_09bfa1bc8001TH4Hc47LpJZh9L  due 2026-09-15T18:14:14.714Z (in 2d)  W8
wku_09bfa1cf70010fRWDB5vhgbejo  due 2026-09-15T18:14:14.982Z (in 2d)  W9
wku_09bfa1dca001a5RnWFBG3dninL  due 2026-09-15T18:14:15.224Z (in 2d)  W10
Owner request

Surface: the agent tools in this repo (packages/opencode/src/tool/ for core tools, packages/opencode/src/kilocode/tool/ for Kilo-specific ones, registered through packages/opencode/src/tool/registry.ts; each tool ships a .ts and a .txt description).

Add a scheduled wakeup tool and its cancel counterpart.

What it must do.

  • The model calls the wakeup tool with a point in time in the future and a reason or prompt to resume with. The harness wakes the model at that time and continues the session from that prompt.
  • A second tool cancels a scheduled wakeup, by its id, and can list what is pending so the model can see what it scheduled.
  • A wakeup survives the process: if the session is idle or the harness restarted, the wake still fires. Say in the PR body how far that guarantee reaches (a running daemon, the next CLI start, or a persisted queue the host drains) and pick the mechanism that already exists in this repo rather than adding a new daemon.

Design decisions the workflow makes, and must state in the PR body.

  • The time argument: accept an absolute time and/or a delay, and define the timezone behaviour and the clamps. There must be a minimum delay and a maximum horizon, and a cap on how many wakeups one session can hold.
  • Where the schedule is stored, and how it is keyed to the session so a wake resumes the right conversation.
  • What the model sees on wake: the prompt it scheduled, plus enough context to know a wakeup fired rather than a user message.
  • Whether an idle session is kept alive or restarted, and what happens if the session ended or its working directory is gone. A wake that cannot resume must fail loudly in a log, never silently disappear.
  • Concurrency: two wakeups due at the same moment, and a wake that fires while the model is already running.

Follow the repo's own conventions.

  • Read several existing tools in both tool directories first, especially one with persisted state and one that talks to the host process (background-process.ts is a good reference), and copy their shape: schema, description file, registration, permission handling, error messages.
  • The tool description (.txt) must tell the model when to use it and when not to, in the same voice as the existing descriptions.

Proof.

  • Unit tests next to the existing tool tests (packages/opencode/test/tool/, packages/opencode/test/kilocode/tool/): schedule, list, cancel, a past time rejected, the horizon and count clamps, a duplicate cancel, and persistence across a restart of whatever holds the schedule.
  • An end-to-end run of the real CLI that schedules a wakeup a short interval ahead, shows the session waking with the scheduled prompt, and shows a cancelled wakeup not firing. Capture the transcript in the PR body.

Surface: the agent tools in this repo (`packages/opencode/src/tool/` for core tools, `packages/opencode/src/kilocode/tool/` for Kilo-specific ones, registered through `packages/opencode/src/tool/registry.ts`; each tool ships a `.ts` and a `.txt` description).

Add a scheduled wakeup tool and its cancel counterpart.

What it must do.
- The model calls the wakeup tool with a point in time in the future and a reason or prompt to resume with. The harness wakes the model at that time and continues the session from that prompt.
- A second tool cancels a scheduled wakeup, by its id, and can list what is pending so the model can see what it scheduled.
- A wakeup survives the process: if the session is idle or the harness restarted, the wake still fires. Say in the PR body how far that guarantee reaches (a running daemon, the next CLI start, or a persisted queue the host drains) and pick the mechanism that already exists in this repo rather than adding a new daemon.

Design decisions the workflow makes, and must state in the PR body.
- The time argument: accept an absolute time and/or a delay, and define the timezone behaviour and the clamps. There must be a minimum delay and a maximum horizon, and a cap on how many wakeups one session can hold.
- Where the schedule is stored, and how it is keyed to the session so a wake resumes the right conversation.
- What the model sees on wake: the prompt it scheduled, plus enough context to know a wakeup fired rather than a user message.
- Wheth
type: "text",
text: text(info),
synthetic: true,
metadata: { background: true, wakeup: true, wakeupID: info.id },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING]: A wake that fires while the session is paused is silently consumed

Setting metadata.background: true routes the wake prompt through KiloSessionControl.background (packages/opencode/src/kilocode/session/control.ts:16). In SessionPrompt.prompt that makes control.begin(sessionID, /*resume*/ false) and then takes the createUserMessage branch; when data.paused is true (set by control.stop on any user cancel/stop, and only cleared by a later normal begin(id, true)), ticket.running() is false and the function returns at packages/opencode/src/session/prompt.ts:1498 without enqueuing loop. resume cannot observe that because the prompt was forked detached, so fireNow deletes the persisted wakeup and nothing is logged. The wake disappears silently, which contradicts the requirement that a wake that cannot resume must fail loudly in a log. Consider verifying the turn actually started (or logging when the prompt returned without running) before treating the fire as delivered.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.


const fireNow = (info: Info) =>
Effect.gen(function* () {
entries.delete(info.id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING]: Duplicate fire when adopt races an in-flight fireNow

fireNow removes the wakeup from the in-memory guards first (lines 81-82) and only deletes the persisted entry after fire.run resolves (line 86). fire.run is not instantaneous: for the production Fire it awaits provide/instance load and the session prompt fork. If adopt runs in that window it re-reads the still-persisted Info, sees neither entries.has nor timers.has (line 148), re-adds it, and arms it again, firing the same wakeup twice. Two processes that open the same directory concurrently (both call adopt against the shared file-backed store) hit the same shape with no timing window at all. Consider claiming the wakeup (remove or atomically rename the persisted entry) before running fire.run, so a second adopt cannot rearm it.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

const pending = yield* storage
.list(["wakeup", String(input.sessionID)])
.pipe(Effect.catch(() => Effect.succeed([] as string[][])))
if (pending.length >= MAX_PER_SESSION) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION]: The per-session cap counts persisted files, not live wakeups

The cap is computed from storage.list(["wakeup", sessionID]), but the service fires from entries. A wakeup that is mid-fire.run still counts (spurious TooMany), and an unreadable/corrupt file is never cleaned up: read swallows the decode error (lines 64-65), adopt skips it (lines 146-147), and lookup/cancel cannot return it either, so it permanently consumes one of the MAX_PER_SESSION slots and cannot be cancelled through the tool. The list-then-write check is also not atomic, so two concurrent schedule calls can both pass at 9 and exceed the cap. Counting the session's entries (and pruning files that fail to decode) would be more robust.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

return info
})

const list = Effect.fn("Wakeup.list")(function* (input?: { sessionID?: SessionID }) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION]: list only reads the in-memory map while cancel falls back to storage

list returns entries only, whereas cancel resolves through lookup, which scans persisted wakeups when the entry is missing. If bootstrap's adopt failed (it is swallowed as a warning in packages/opencode/src/kilocode/bootstrap.ts:65-69) or a wakeup was written by another process, list confidently reports "No pending wakeups for this session" while cancel still finds and removes the same id. A storage-backed fallback in list (or not swallowing the adopt failure) would remove the inconsistency.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.


// Cancel is idempotent: an already-fired, already-cancelled, or
// unknown id is reported, never thrown.
const removed = yield* wake.cancel(id as Wakeup.ID)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION]: cancel is not scoped to the caller's session

list filters by ctx.sessionID (line 58), but cancel passes only the id: Wakeup.cancel/lookup scan every stored wakeup and never compare info.sessionID to the caller (packages/opencode/src/kilocode/wakeup/index.ts:67-77, 130-141). A tool call in one session can therefore cancel another session's wakeup, and the description promises "the wakeups scheduled for this session". Passing ctx.sessionID through to cancel/lookup would make the two actions consistent.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

import { Context, Effect, Schema, Types } from "effect"
import z from "zod"

/** A scheduled wakeup never fires sooner than this after it is scheduled. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION]: The MIN_DELAY_MS doc comment overstates the guarantee

resolve applies the minimum only on the delay branch (Math.max(now + span, now + MIN_DELAY_MS), line 121); an absolute when only has to be > now, so when = now + 1s fires in about a second. The .txt description correctly scopes the minimum to delay, but this comment ("never fires sooner than this after it is scheduled") - and the user-facing changelog that repeats it - implies every wakeup. Either scope the comment/changelog to delay or clamp when too.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Review and cancel the wakeups scheduled for this session.

Use this tool to:
- List the wakeups you scheduled, with their id, due time, and prompt

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION]: Description says the list shows the prompt, but it shows the reason

line() renders info.reason ?? info.prompt (packages/opencode/src/kilocode/tool/cancel-wakeup.ts:45), so whenever a reason was supplied the scheduled prompt is not shown. Either state that the label/reason is listed (falling back to the prompt), or include the prompt in the row.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

await Instance.restore(inst, fn)
return
}
await provide({ directory: info.directory, fn })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION]: The provide fallback is unreachable, so a reloaded instance is never re-resolved

arm is always called from a context that already has the instance's InstanceRef (schedule from the tool, adopt from bootstrap with InstanceRef provided at packages/opencode/src/project/instance-store.ts:64), and adopt only arms wakes whose info.directory equals the instance directory. That makes inst.directory === info.directory true for every armed wake, so this provide path never runs and a long-horizon timer always restores the InstanceContext captured when it was armed. If that directory was later reloaded or disposed, the stale context is reused instead of re-loading the current one. Is that intended, or should the guard compare something that can actually go stale?


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

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/resume.ts 37 A wake that fires while the session is paused is silently consumed with no error log.
packages/opencode/src/kilocode/wakeup/index.ts 81 Duplicate fire when adopt races an in-flight fireNow (persistence removed after fire.run).

SUGGESTION

File Line Issue
packages/opencode/src/kilocode/wakeup/index.ts 105 Cap counts persisted files, so corrupt files permanently consume slots; check is not atomic.
packages/opencode/src/kilocode/wakeup/index.ts 124 list reads only the in-memory map while cancel falls back to storage.
packages/opencode/src/kilocode/tool/cancel-wakeup.ts 79 cancel is not scoped to the caller's session, unlike list.
packages/opencode/src/kilocode/wakeup/schema.ts 8 MIN_DELAY_MS comment/changelog overstate the minimum, which only applies to delay.
packages/opencode/src/kilocode/tool/cancel-wakeup.txt 4 Says the list shows the prompt, but it renders reason ?? prompt.
packages/opencode/src/kilocode/wakeup/resume.ts 53 The provide fallback is unreachable, so a reloaded instance is never re-resolved.
Files Reviewed (20 files)
  • .changeset/scheduled-wakeup.md
  • packages/opencode/src/effect/app-runtime.ts
  • packages/opencode/src/kilocode/bootstrap.ts
  • packages/opencode/src/kilocode/tool/cancel-wakeup.ts
  • packages/opencode/src/kilocode/tool/cancel-wakeup.txt
  • packages/opencode/src/kilocode/tool/registry.ts
  • packages/opencode/src/kilocode/tool/schedule-wakeup.ts
  • packages/opencode/src/kilocode/tool/schedule-wakeup.txt
  • packages/opencode/src/kilocode/wakeup/index.ts
  • packages/opencode/src/kilocode/wakeup/resume.ts
  • packages/opencode/src/kilocode/wakeup/schema.ts
  • packages/opencode/src/tool/registry.ts
  • packages/opencode/test/kilocode/tool-registry-indexing.test.ts
  • packages/opencode/test/kilocode/tool/cancel-wakeup.test.ts
  • packages/opencode/test/kilocode/tool/schedule-wakeup.test.ts
  • packages/opencode/test/kilocode/wakeup/wakeup-resume.test.ts
  • packages/opencode/test/kilocode/wakeup/wakeup.test.ts
  • packages/opencode/test/tool/registry.test.ts
  • patches/[email protected]
  • script/check-opencode-promise-facades.ts

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 marked this pull request as draft September 13, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant