Skip to content

feat(core): add Code Mode checks with static tool scanning to schedules - #60140

Merged
kitlangton merged 1 commit into
devfrom
schedules-code-checks
Oct 29, 2026
Merged

kitlangton merged 1 commit into
devfrom
schedules-code-checks

Conversation

@kitlangton

@kitlangton kitlangton commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Schedules can now run a Code Mode JavaScript check (if: { code } or until: { code }) before waking a session, using the same state and unattended lifecycle as shell checks, with no model call and no allow list to maintain.
Builds on #60137 (merged). Everything stays behind experimental.schedules, and requires no new database migration.

💡 Why
Shell checks cover local CLIs, while integrations like Slack, Linear, and MCP servers hold OAuth credentials inside OpenCode and often want structured JSON cursor state between checks.

🆕 Now you can

  • Pass { code: "..." } to if or until on schedule_create or /api/experimental/schedule. OpenCode walks the Acorn AST at creation (CodeMode.scanTools) to find every tools.<path>(...) call statically, rejects dynamic tools[x] or aliasing, authorizes those tools once without saving a project-wide grant, and exposes only those tools (with no fetch) on unattended ticks.
  • Read state, now and runs directly as globals (seeded at creation on every and until so events before the first tick aren't missed), and return either a boolean (until) or { wake, state?, message? }.
  • Keep recursive or file-editing tools (opencode.schedule_*, session_move, worktree_*, and edit tools) out of unattended checks automatically.

🚦 How it ships
Off by default behind experimental.schedules, same as #59401, #59457 and #60137.

  schedule_create({ when, if / until: { code }, prompt })
+   -> CodeMode.scanTools(code) extracts static `tools.<path>(...)` calls from the AST
+   -> authorize extracted tools once (no `save`), seed initial `state` at t = 0
+   -> on each tick: run confined Code Mode over only extracted tools (no `fetch`, no model call)
    -> wake only when `wake: true`, appending `message` to `prompt`

📊 Proof: 4 new AST scanner and binding tests in @opencode/codemode, 46 core schedule/tool/codemode tests (covering t=0 state seeding, wake-only ends.times, boolean and state-free until returns, fetch absence, static tool extraction, one-time approval, denied secondary permissions, and forbidden recursive/edit tools), and 1 end-to-end server API test.

👀 Read: packages/codemode/src/scan.ts (scanTools) and authorizeCode / runCode in packages/core/src/schedule/check.ts. Tool.Service stays free of Permission.Service: ScheduleCheck owns the Permission.Unattended scope, while Tool.check only filters the inventory. The rest is routine (schema, OpenAPI, generated client).

Planned feature · no user reports · not measured

Checks run
  • bun test test/scan.test.ts (codemode): 4 pass, 0 fail
  • bun run script/test.ts test/schedule.test.ts test/tool-schedule.test.ts test/codemode.test.ts (core): 46 pass, 0 fail
  • bun test test/schedule.test.ts (server): 1 pass, 0 fail
  • bun run check (full repo lint, sdk-docs, and 35-package turbo typecheck): pass

@kitlangton
kitlangton force-pushed the schedules-code-checks branch from 4aacd88 to 3ade006 Compare October 29, 2026 07:31
@kitlangton
kitlangton force-pushed the schedules-shell-checks branch from 2f21615 to 64d6f83 Compare October 29, 2026 16:58
Base automatically changed from schedules-shell-checks to dev October 29, 2026 17:10
@kitlangton
kitlangton force-pushed the schedules-code-checks branch from 3ade006 to 5cb8a99 Compare October 29, 2026 18:36
@kitlangton
kitlangton merged commit c24c5af into dev Oct 29, 2026
36 of 37 checks passed
@kitlangton
kitlangton deleted the schedules-code-checks branch October 29, 2026 19:29
@amrkmn amrkmn mentioned this pull request Oct 30, 2026
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant