Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6654e1e
docs(cron-jobs): plan staged rollout
zerob13 Jul 2, 2026
8114a9b
feat(cron-jobs): add phase one scheduler
zerob13 Jul 2, 2026
5f683fe
feat(cron-jobs): add cron schedule engine
zerob13 Jul 3, 2026
0977931
feat(cron-jobs): add schedule presets
zerob13 Jul 3, 2026
533b705
feat(cron-jobs): add agent binding
zerob13 Jul 3, 2026
2c0836f
fix(cron-jobs): clone upsert payloads
zerob13 Jul 3, 2026
464f45d
feat(cron-jobs): add timezone selector
zerob13 Jul 3, 2026
3b8a1e2
docs(cron-jobs): record cron editor choice
zerob13 Jul 3, 2026
e43e75a
feat(cron-jobs): add visual cron editor
zerob13 Jul 3, 2026
337b4cf
fix(cron-jobs): simplify cron expression UI
zerob13 Jul 3, 2026
41aac9b
feat(cron-jobs): start runs in sessions
zerob13 Jul 3, 2026
420b68f
fix(cron-jobs): align run insert values
zerob13 Jul 3, 2026
6bae72a
feat(cron-jobs): show run history
zerob13 Jul 3, 2026
8549892
fix(cron-jobs): simplify scheduled UI
zerob13 Jul 3, 2026
b00aaef
build: add pnpm build approvals
zerob13 Jul 3, 2026
8344843
feat(cron-jobs): store session metadata
zerob13 Jul 3, 2026
4418a1d
fix(cron-jobs): focus main on open
zerob13 Jul 3, 2026
505e906
fix(cron-jobs): remove run open button
zerob13 Jul 3, 2026
61acf20
fix(cron-jobs): refresh heartbeat status
zerob13 Jul 3, 2026
a03961c
fix(cron-jobs): ignore idle exit error
zerob13 Jul 3, 2026
ffdd077
fix(cron-jobs): isolate scheduler db open
zerob13 Jul 3, 2026
ac00eb9
feat(cron-jobs): add run routes
zerob13 Jul 3, 2026
523523d
fix(cron-jobs): refresh scheduled run history
zerob13 Jul 3, 2026
800c2d1
feat(cron-jobs): add delivery persistence
zerob13 Jul 3, 2026
52ff166
feat(cron-jobs): route desktop deliveries
zerob13 Jul 3, 2026
758db46
feat(cron-jobs): add delivery controls
zerob13 Jul 3, 2026
9cef686
fix(cron-jobs): use remote delivery targets
zerob13 Jul 3, 2026
65f6cbd
feat(cron-jobs): show delivery receipts
zerob13 Jul 3, 2026
eb7fff0
fix(cron-jobs): suppress skip delivery
zerob13 Jul 4, 2026
94ef3fc
fix(startup): unblock mcp and cron jobs
zerob13 Jul 4, 2026
d955ad3
fix(cron): stabilize scheduled delivery
zerob13 Jul 4, 2026
3bbff37
feat(cron): finish scheduled task phases
zerob13 Jul 4, 2026
afd29ec
fix(cron): cap task prompt editor height
zerob13 Jul 4, 2026
2a937fa
docs(cron): clean completed sdd tasks
zerob13 Jul 4, 2026
1836697
fix(i18n): complete cronJobs translations for all languages
zerob13 Jul 4, 2026
04392b6
fix(cron): address pr review feedback
zerob13 Jul 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ flowchart LR
| `LLMProviderPresenter` | `src/main/presenter/llmProviderPresenter/` | provider 实例、model/runtime 管理、ACP helper、AI SDK runtime |
| `StartupWorkloadCoordinator` | `src/main/presenter/startupWorkloadCoordinator/` | startup/settings/floating 等目标的分阶段后台任务调度 |
| `RemoteControlPresenter` | `src/main/presenter/remoteControlPresenter/` | Telegram、Feishu/Lark、QQBot、Discord、WeChat iLink 远程控制 |
| `ScheduledTasksService` | `src/main/presenter/scheduledTasks/` | 一次性、每日、每周任务调度和 prompt/notify action dispatch |
| `CronJobsService` | `src/main/presenter/cronJobs/` | 定时任务持久化、cron 调度、Agent run 执行和 Remote 投递 |
| `DatabaseSecurityPresenter` | `src/main/presenter/databaseSecurityPresenter/` | SQLCipher 启用、改密、关闭、safeStorage/manual unlock |
| Spotlight search | `src/renderer/src/stores/ui/spotlight.ts` | 全局搜索、会话/消息跳转、设置导航和非破坏性 action |

Expand Down
32 changes: 16 additions & 16 deletions docs/FLOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,24 +187,24 @@ sequenceDiagram

```mermaid
sequenceDiagram
participant UI as Settings Scheduled Tasks
participant Client as ScheduledTasksClient
participant Service as ScheduledTasksService
participant Notify as NotificationPresenter
participant Session as Session creator

UI->>Client: list/upsert/toggle/delete/fireNow
Client->>Service: scheduledTasks.* route
Service->>Service: compute next fire time
alt notify action
Service->>Notify: showNotification
else prompt action
Service->>Session: create session, optional autoSend
end
participant UI as Settings Scheduled
participant Client as CronJobsClient
participant Service as CronJobsService
participant Utility as Scheduler utility
participant Agent as AgentSessionPresenter
participant Remote as RemoteControlPresenter

UI->>Client: list/upsert/toggle/runNow
Client->>Service: cronJobs.* route
Service->>Utility: reconcile enabled jobs
Utility->>Service: RUN_DUE
Service->>Agent: create detached session and send task prompt
Agent-->>Service: run status and output updates
Service->>Remote: optional notification-only delivery
```

Triggers 支持 once、daily、weekly;actions 支持 notification 和 prompt。Prompt action 可指定
agent、provider、model、system prompt,并通过 route runtime 创建会话
Triggers 使用 cron 表达式。每次触发创建独立 detached session;Remote 投递只发送通知,不进入普通
Remote 会话上下文

## 9. Remote Control

Expand Down
91 changes: 91 additions & 0 deletions docs/features/cron-agent-jobs-phase-1-scheduler/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Cron Agent Jobs Phase 1: Scheduler Process

## User Need

Users need a reliable Cron Jobs foundation that can discover due jobs without tying scheduling
accuracy to renderer lifetime, settings UI state, or one main-process timeout per task.

## Goal

Introduce the first independently mergeable slice of the Cron Jobs / Agent Jobs module:

- A SQLite-backed `cron_jobs` and `cron_job_runs` store.
- A main-process `SchedulerProcessManager`.
- An Electron `utilityProcess` named `deepchat-scheduler`.
- A typed scheduler protocol where the utility process only discovers due jobs and queues runs.
- A minimal Cron Jobs status UI that shows scheduler state.

The main process owns job execution. In this phase execution remains a mock action after receiving
`RUN_DUE`; no agent runtime work is performed yet.

## Current State

The removed legacy scheduled-task service used ConfigPresenter timers. Cron Jobs is the only
scheduler surface going forward and stores schedules in SQLite.

## Acceptance Criteria

- With zero enabled cron jobs, `deepchat-scheduler` is stopped.
- Creating or enabling the first cron job starts `deepchat-scheduler` after the app is ready.
- Disabling or deleting the last enabled cron job stops the scheduler within 30 seconds.
- The scheduler scans only `next_run_at <= Date.now()` jobs and creates `queued` runs.
- Queued run creation is idempotent for the same `(job_id, scheduled_at)` slot.
- Main receives `RUN_DUE` with `jobId` and `runId`.
- App startup and OS resume trigger `RECONCILE`.
- Utility-process crash is detected and restarted with bounded backoff while enabled jobs remain.
- Scheduler status is available through a typed route and rendered in the Cron Jobs page.
- The Cron Jobs page renders schedule-derived next runs as a read-only indicator, not a
user-editable schedule setting.

## UX Shape

Running state:

```text
+---------------------------------------------------------+
| Cron Jobs |
| Scheduler: Running | utilityProcess | pid 18421 |
| Enabled jobs: 2 | Next run: 2026-07-03 09:00 |
| |
| [New Job] [Restart Timer] |
+---------------------------------------------------------+
```

Stopped state:

```text
+---------------------------------------------------------+
| Cron Jobs |
| Scheduler: Stopped |
| Create or enable a cron job to start the scheduler. |
| |
| [New Job] |
+---------------------------------------------------------+
```

## Non-Goals

- No cron expression parser.
- No schedule editor beyond minimal data needed to exercise scheduler state.
- No agent binding, permission resolution, or real session execution.
- No delivery, remote continuation, or `cronjob` agent tool.
- Legacy scheduled-task cleanup is handled by `docs/issues/remove-legacy-scheduled-tasks`.

## Constraints

- Use typed routes, typed contracts, and renderer `api/*Client`.
- Do not introduce legacy IPC or `useLegacyPresenter()` paths.
- The scheduler utility process must not execute model, tool, notification, or remote delivery logic.
- If the database is encrypted, the utility process must open its own connection through a
narrowly scoped scheduler DB adapter and must not log database credentials.
- `utilityProcess.fork()` must be invoked only after Electron app `ready`.

## References

- Electron `utilityProcess` creates a Node-enabled child process and supports message passing:
https://www.electronjs.org/docs/latest/api/utility-process

## Open Questions

None. Phase 1 deliberately keeps execution mocked so later phases can replace the mock without
reworking the scheduler lifecycle.
110 changes: 110 additions & 0 deletions docs/features/cron-agent-jobs-phase-2-cron-trigger-engine/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Cron Agent Jobs Phase 2: Cron Trigger Engine

## User Need

Users need one expressive schedule model instead of separate once/daily/weekly trigger kinds.
The schedule must preview upcoming runs, respect timezones, and handle missed runs predictably.

## Goal

Replace phase 1's timestamp-only scheduling input with a cron-expression trigger engine:

- Store every schedule as `cronExpr + timezone`.
- Validate expressions through a dedicated parser.
- Compute and persist `next_run_at`.
- Preview the next N runs in the UI and tool-facing service layer.
- Support misfire policy for missed runs.
- Provide preset controls that only generate cron expressions.

## Parser Choice

Use `cron-parser` unless implementation discovers a blocker in the locked package version.
The package is not currently in `package.json`, so this phase adds the dependency and tests the
exact syntax DeepChat exposes.

The exposed syntax must be limited to parser-backed behavior verified by tests. Do not market a
cron feature in UI copy without a passing unit test for that feature.

## Schedule Model

```ts
type CronJobSchedule = {
cronExpr: string
timezone: string
misfirePolicy: 'skip' | 'run_once'
maxCatchUpRuns?: number
}
```

Presets are UI helpers only:

```ts
type SchedulePreset =
| { type: 'every_n_minutes'; n: number }
| { type: 'hourly'; minute: number }
| { type: 'daily'; time: string }
| { type: 'weekdays'; time: string }
| { type: 'weekly'; days: number[]; time: string }
| { type: 'monthly'; day: number | 'last'; time: string }
| { type: 'custom'; cronExpr: string }
```

## Acceptance Criteria

- `*/5 * * * *` previews the next 5 runs from a controlled clock.
- `0 9 * * 1-5` correctly represents weekdays at 09:00.
- `0 0 9 L * *` correctly represents the last day of each month at 09:00 when supported by the
locked parser version.
- `0 0 9 * * 1#1` correctly represents the first Monday of each month when supported by the locked
parser version.
- Timezone changes immediately update preview rows and persisted `next_run_at`.
- Invalid cron input shows a parser error and cannot enable the job.
- Scheduler due scans remain based only on `next_run_at <= now`.
- `misfirePolicy: 'skip'` advances to the next future run after downtime.
- `misfirePolicy: 'run_once'` creates at most one catch-up run per reconcile unless
`maxCatchUpRuns` is explicitly set.

## UX Shape

```text
+---------------------------------------------------------+
| Schedule |
| Mode |
| (o) Preset |
| Every [day v] at [09:00] |
| |
| ( ) Cron expression |
| [0 0 9 * * * ] |
| Timezone [Asia/Tokyo v] |
| |
| Next 5 runs |
| - 2026-07-03 09:00 |
| - 2026-07-04 09:00 |
| - 2026-07-05 09:00 |
+---------------------------------------------------------+
```

## Non-Goals

- No agent binding or real execution.
- No run detail UI beyond schedule preview.
- No remote delivery or `cronjob` agent tool.
- No broad legacy task migration unless the migration can be completed without changing execution
semantics.

## Constraints

- Do not reintroduce once/daily/weekly as persisted model variants.
- Presets must write only `cronExpr` and `timezone`.
- Store timestamps as epoch milliseconds.
- Use deterministic tests with fixed reference dates and timezones.

## References

- `cron-parser` README documents timezone handling and extended syntax including `L`, `#`, and `H`:
https://github.com/harrisiirak/cron-parser/blob/master/README.md

## Open Questions

None. Unsupported parser syntax should be removed from UI copy instead of becoming a product
promise.
102 changes: 102 additions & 0 deletions docs/features/cron-agent-jobs-phase-3-agent-binding/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Cron Agent Jobs Phase 3: Agent Binding

## User Need

Users need a scheduled job to run as a specific DeepChat agent, with model, tools, MCP servers,
skills, permission mode, and workspace behavior resolved from that agent.

## Goal

Turn a scheduled cron job into an agent-bound job definition:

- Every enabled job must reference a valid `agentId`.
- Runtime settings default to following the current agent config.
- Snapshot policies preserve a stable runtime when requested.
- Deleted or disabled agents make affected jobs invalid instead of silently running a fallback.

## Job Model

```ts
type AgentCronJob = {
id: string
name: string
description?: string
enabled: boolean
status: 'ready' | 'disabled' | 'invalid_agent'

schedule: {
cronExpr: string
timezone: string
misfirePolicy: 'skip' | 'run_once'
}

agent: {
agentId: string
agentVersion?: string
modelPolicy: 'follow_agent' | 'pin_current'
toolPolicy: 'follow_agent' | 'snapshot'
permissionPolicy: 'follow_agent' | 'snapshot'
}

task: {
prompt: string
systemInstruction?: string
outputMode: 'final_message' | 'structured_json' | 'artifact'
}

runtime: {
maxDurationMs: number
maxTurns: number
concurrencyPolicy: 'skip' | 'queue'
}

nextRunAt: number | null
}
```

## Acceptance Criteria

- Creating or enabling a job requires a valid enabled agent.
- Job execution planning resolves model/tools/MCP/skills/permissions from the bound agent.
- In `follow_agent` mode, agent config updates affect the next job run.
- In snapshot mode, the job uses the captured runtime snapshot.
- Deleting or disabling an agent moves related jobs to `invalid_agent` and prevents enqueueing.
- The job editor clearly shows which runtime parts follow the agent and which are pinned.
- The task content field grows only up to 10 text rows and scrolls internally beyond that.
- Scheduler status excludes invalid jobs from enabled runnable counts.

## UX Shape

```text
+---------------------------------------------------------+
| Agent Binding |
| Agent: [DeepChat Issue Triage v] |
| |
| Runtime follows agent |
| [x] Model |
| [x] Tools / MCP / Skills |
| [x] Permission mode |
| |
| Advanced |
| [ ] Pin current model/tools/permissions |
+---------------------------------------------------------+
```

## Non-Goals

- No fresh session creation yet.
- No remote delivery.
- No `cronjob` agent tool.
- No support for running without an agent.

## Constraints

- Use `AgentRepository` and existing agent config normalization.
- Keep `deepchat` as an explicit agent selection, not an invisible fallback.
- Snapshot JSON must be versioned so later migrations can identify the captured shape.
- Do not expose raw provider credentials in snapshots.

## Open Questions

None. Snapshot support is required for model/tools/permissions at the level available through the
current agent config; unavailable runtime internals should remain follow-mode only.
Loading