@agentick/scheduler 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,183 @@
1
+ # @agentick/scheduler
2
+
3
+ Scheduled jobs for Agentick agents. Pluggable backends, file-based persistence,
4
+ crash recovery.
5
+
6
+ ## Install
7
+
8
+ ```sh
9
+ pnpm add @agentick/scheduler
10
+ ```
11
+
12
+ ## Quick Start
13
+
14
+ ```typescript
15
+ import { createClient } from "@agentick/client";
16
+ import { CronService, createScheduleTool, bindSchedulerStore } from "@agentick/scheduler";
17
+
18
+ const cronService = new CronService({
19
+ dataDir: ".myagent",
20
+ client,
21
+ defaultTarget: "tui",
22
+ });
23
+ await cronService.start();
24
+
25
+ // Give the agent a tool to manage its own schedule
26
+ const ScheduleTool = createScheduleTool(cronService.store);
27
+ ```
28
+
29
+ ## Architecture
30
+
31
+ ```
32
+ CronService
33
+ ├── JobStore (persistent job definitions)
34
+ ├── SchedulerBackend (pluggable — when to fire)
35
+ │ └── NodeCronBackend (default, in-process timers)
36
+ └── TriggerWatcher (optional) (external trigger pickup)
37
+ ```
38
+
39
+ **JobStore** persists jobs as individual JSON files in `<dataDir>/jobs/`.
40
+ Survives crashes.
41
+
42
+ **SchedulerBackend** is the pluggable timer mechanism. When a job fires, it
43
+ calls the `onFire` callback provided by CronService, which dispatches to the
44
+ target session via `client.session(target).send()`.
45
+
46
+ **TriggerWatcher** watches `<dataDir>/triggers/` for externally-written JSON
47
+ files. System cron, scripts, webhooks — anything that writes a trigger file
48
+ wakes the agent. Enabled by default, opt out with `watchExternalTriggers: false`.
49
+
50
+ ## Backend Interface
51
+
52
+ ```typescript
53
+ interface SchedulerBackend {
54
+ start(): Promise<void>;
55
+ stop(): Promise<void>;
56
+ schedule(job: Job, onFire: () => Promise<void>): void;
57
+ unschedule(jobId: string): void;
58
+ }
59
+ ```
60
+
61
+ Four methods. `start`/`stop` for lifecycle. `schedule`/`unschedule` for per-job
62
+ timer management. The backend decides everything about HOW and WHEN — timers,
63
+ durability, retries. Dispatch is CronService's job.
64
+
65
+ ### NodeCronBackend (default)
66
+
67
+ In-process scheduling via `node-cron`. Writes trigger files for crash recovery —
68
+ if the process dies between fire and dispatch, trigger files persist and are
69
+ drained on next `start()`.
70
+
71
+ ```typescript
72
+ import { createNodeCronBackend } from "@agentick/scheduler";
73
+
74
+ const backend = createNodeCronBackend({ triggersDir: ".myagent/triggers" });
75
+ ```
76
+
77
+ ### Custom Backends
78
+
79
+ Implement `SchedulerBackend` for any timer source. Durability is the
80
+ backend's problem — not the framework's.
81
+
82
+ ```typescript
83
+ // BullMQ example (conceptual)
84
+ const bullBackend: SchedulerBackend = {
85
+ async start() {
86
+ /* connect to Redis, start worker */
87
+ },
88
+ async stop() {
89
+ /* close connections */
90
+ },
91
+ schedule(job, onFire) {
92
+ /* create repeatable BullMQ job */
93
+ },
94
+ unschedule(jobId) {
95
+ /* remove BullMQ job scheduler */
96
+ },
97
+ };
98
+
99
+ const service = new CronService({
100
+ dataDir: ".myagent",
101
+ client,
102
+ backend: bullBackend,
103
+ watchExternalTriggers: false,
104
+ });
105
+ ```
106
+
107
+ ## Agent Tool
108
+
109
+ `createScheduleTool` returns a tool the agent uses to manage its schedule.
110
+ Actions: `add`, `list`, `remove`, `enable`, `disable`.
111
+
112
+ ```tsx
113
+ import { createScheduleTool } from "@agentick/scheduler";
114
+
115
+ const ScheduleTool = createScheduleTool(cronService.store);
116
+
117
+ // In your component tree:
118
+ <ScheduleTool />;
119
+ ```
120
+
121
+ The tool's `render` function puts active jobs in context so the model knows
122
+ what's already scheduled.
123
+
124
+ ## Heartbeat
125
+
126
+ A heartbeat is a recurring job that reads a file and prompts the agent to act
127
+ on its contents. If the file is empty or missing, the trigger is skipped.
128
+
129
+ ```typescript
130
+ import { createHeartbeatJob } from "@agentick/scheduler";
131
+
132
+ cronService.store.create({
133
+ ...createHeartbeatJob({
134
+ cron: "*/5 * * * *",
135
+ target: "tui",
136
+ heartbeatFile: ".myagent/HEARTBEAT.md",
137
+ }),
138
+ id: "heartbeat",
139
+ });
140
+ ```
141
+
142
+ ## External Triggers
143
+
144
+ Anything that writes a JSON file to the triggers directory wakes the agent.
145
+
146
+ ```bash
147
+ echo '{"target":"tui","prompt":"wake up","jobId":"manual","jobName":"manual","firedAt":"2025-01-01T00:00:00Z","oneshot":true}' \
148
+ > .myagent/triggers/$(date +%s)-manual.json
149
+ ```
150
+
151
+ ## CronService Options
152
+
153
+ ```typescript
154
+ interface CronServiceOptions {
155
+ dataDir: string; // Root dir — jobs/ and triggers/ live here
156
+ client: AgentickClient; // Client for sending messages to sessions
157
+ backend?: SchedulerBackend; // Default: NodeCronBackend
158
+ watchExternalTriggers?: boolean; // Default: true
159
+ defaultTarget?: string; // Fallback session ID
160
+ onTriggerProcessed?: (trigger: Trigger) => void;
161
+ onError?: (error: Error, context: string) => void;
162
+ }
163
+ ```
164
+
165
+ ## Exports
166
+
167
+ ```typescript
168
+ export { CronService } from "./cron-service.js";
169
+ export { JobStore } from "./job-store.js";
170
+ export { createNodeCronBackend } from "./node-cron-backend.js";
171
+ export { TriggerWatcher } from "./trigger-watcher.js";
172
+ export { createScheduleTool } from "./schedule-tool.js";
173
+ export { createHeartbeatJob } from "./heartbeat.js";
174
+ export { bindSchedulerStore, getSchedulerStore } from "./bridge.js";
175
+ export type {
176
+ Job,
177
+ Trigger,
178
+ SchedulerBackend,
179
+ CronServiceOptions,
180
+ HeartbeatOptions,
181
+ CreateJobInput,
182
+ } from "./types.js";
183
+ ```
@@ -0,0 +1,4 @@
1
+ import type { JobStore } from "./job-store.js";
2
+ export declare function bindSchedulerStore(store: JobStore): void;
3
+ export declare function getSchedulerStore(): JobStore | null;
4
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI/C,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAExD;AAED,wBAAgB,iBAAiB,IAAI,QAAQ,GAAG,IAAI,CAEnD"}
package/dist/bridge.js ADDED
@@ -0,0 +1,8 @@
1
+ let _store = null;
2
+ export function bindSchedulerStore(store) {
3
+ _store = store;
4
+ }
5
+ export function getSchedulerStore() {
6
+ return _store;
7
+ }
8
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAEA,IAAI,MAAM,GAAoB,IAAI,CAAC;AAEnC,MAAM,UAAU,kBAAkB,CAAC,KAAe;IAChD,MAAM,GAAG,KAAK,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Job, CronServiceOptions, HeartbeatOptions } from "./types.js";
2
+ import { JobStore } from "./job-store.js";
3
+ import { TriggerWatcher } from "./trigger-watcher.js";
4
+ import type { SchedulerBackend } from "./types.js";
5
+ export declare class CronService {
6
+ readonly store: JobStore;
7
+ readonly backend: SchedulerBackend;
8
+ readonly watcher: TriggerWatcher | null;
9
+ private readonly client;
10
+ private readonly defaultTarget;
11
+ private readonly onProcessed;
12
+ private readonly onError;
13
+ private scheduledIds;
14
+ constructor(options: CronServiceOptions);
15
+ start(): Promise<void>;
16
+ stop(): Promise<void>;
17
+ /** Ensure a heartbeat job exists — idempotent */
18
+ ensureHeartbeat(options?: HeartbeatOptions): Job;
19
+ private _onStoreChange;
20
+ private _dispatch;
21
+ }
22
+ //# sourceMappingURL=cron-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron-service.d.ts","sourceRoot":"","sources":["../src/cron-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,qBAAa,WAAW;IACtB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2C;IACvE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,YAAY,CAAqB;gBAE7B,OAAO,EAAE,kBAAkB;IAuBjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IActB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAM3B,iDAAiD;IACjD,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,GAAG;IAShD,OAAO,CAAC,cAAc,CAcpB;YAEY,SAAS;CAyDxB"}
@@ -0,0 +1,121 @@
1
+ import { join } from "node:path";
2
+ import { JobStore } from "./job-store.js";
3
+ import { TriggerWatcher } from "./trigger-watcher.js";
4
+ import { createNodeCronBackend } from "./node-cron-backend.js";
5
+ import { createHeartbeatJob } from "./heartbeat.js";
6
+ export class CronService {
7
+ store;
8
+ backend;
9
+ watcher;
10
+ client;
11
+ defaultTarget;
12
+ onProcessed;
13
+ onError;
14
+ scheduledIds = new Set();
15
+ constructor(options) {
16
+ const jobsDir = join(options.dataDir, "jobs");
17
+ const triggersDir = join(options.dataDir, "triggers");
18
+ this.client = options.client;
19
+ this.defaultTarget = options.defaultTarget;
20
+ this.onProcessed = options.onTriggerProcessed;
21
+ this.onError = options.onError;
22
+ this.store = new JobStore(jobsDir);
23
+ this.backend = options.backend ?? createNodeCronBackend({ triggersDir });
24
+ // External trigger watcher — enabled by default, opt out with watchExternalTriggers: false
25
+ const watchExternal = options.watchExternalTriggers ?? true;
26
+ this.watcher = watchExternal
27
+ ? new TriggerWatcher(triggersDir, options.client, this.store, {
28
+ defaultTarget: options.defaultTarget,
29
+ onTriggerProcessed: options.onTriggerProcessed,
30
+ onError: options.onError,
31
+ })
32
+ : null;
33
+ }
34
+ async start() {
35
+ // Register all enabled jobs with the backend
36
+ for (const job of this.store.listEnabled()) {
37
+ this.backend.schedule(job, () => this._dispatch(job));
38
+ this.scheduledIds.add(job.id);
39
+ }
40
+ // React to store changes
41
+ this.store.on("change", this._onStoreChange);
42
+ await this.backend.start();
43
+ await this.watcher?.start();
44
+ }
45
+ async stop() {
46
+ this.store.off("change", this._onStoreChange);
47
+ await this.backend.stop();
48
+ this.watcher?.stop();
49
+ }
50
+ /** Ensure a heartbeat job exists — idempotent */
51
+ ensureHeartbeat(options) {
52
+ const existing = this.store.get("heartbeat");
53
+ if (existing)
54
+ return existing;
55
+ return this.store.create({
56
+ ...createHeartbeatJob(options),
57
+ id: "heartbeat",
58
+ });
59
+ }
60
+ _onStoreChange = () => {
61
+ const enabled = new Map(this.store.listEnabled().map((j) => [j.id, j]));
62
+ // Unschedule everything currently tracked
63
+ for (const id of this.scheduledIds) {
64
+ this.backend.unschedule(id);
65
+ }
66
+ this.scheduledIds.clear();
67
+ // Re-schedule enabled jobs
68
+ for (const [, job] of enabled) {
69
+ this.backend.schedule(job, () => this._dispatch(job));
70
+ this.scheduledIds.add(job.id);
71
+ }
72
+ };
73
+ async _dispatch(job) {
74
+ // Read fresh state — job may have been updated since scheduling
75
+ const current = this.store.get(job.id);
76
+ if (!current || !current.enabled)
77
+ return;
78
+ const target = current.target || this.defaultTarget;
79
+ if (!target) {
80
+ this.onError?.(new Error(`Job "${current.id}" has no target and no default configured`), "dispatch");
81
+ return;
82
+ }
83
+ try {
84
+ const session = this.client.session(target);
85
+ const handle = session.send({
86
+ messages: [
87
+ {
88
+ role: "event",
89
+ content: [{ type: "text", text: current.prompt }],
90
+ metadata: {
91
+ source: { type: "cron" },
92
+ event_type: "cron_trigger",
93
+ job_id: current.id,
94
+ job_name: current.name,
95
+ fired_at: new Date().toISOString(),
96
+ },
97
+ },
98
+ ],
99
+ });
100
+ await handle.result;
101
+ // Update lastFiredAt
102
+ this.store.update(current.id, { lastFiredAt: new Date().toISOString() });
103
+ // Oneshot: delete after successful fire
104
+ if (current.oneshot) {
105
+ this.store.delete(current.id);
106
+ }
107
+ this.onProcessed?.({
108
+ jobId: current.id,
109
+ jobName: current.name,
110
+ target,
111
+ prompt: current.prompt,
112
+ firedAt: new Date().toISOString(),
113
+ oneshot: current.oneshot,
114
+ });
115
+ }
116
+ catch (error) {
117
+ this.onError?.(error instanceof Error ? error : new Error(String(error)), `dispatch:${current.id}`);
118
+ }
119
+ }
120
+ }
121
+ //# sourceMappingURL=cron-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron-service.js","sourceRoot":"","sources":["../src/cron-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { HeartbeatOptions, CreateJobInput } from "./types.js";
2
+ export declare function createHeartbeatJob(opts?: HeartbeatOptions): CreateJobInput;
3
+ //# sourceMappingURL=heartbeat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../src/heartbeat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEnE,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,cAAc,CAa1E"}
@@ -0,0 +1,14 @@
1
+ export function createHeartbeatJob(opts) {
2
+ return {
3
+ name: "heartbeat",
4
+ cron: opts?.cron ?? "*/5 * * * *",
5
+ target: opts?.target ?? "tui",
6
+ prompt: "[heartbeat] Review your heartbeat file and act on any due, prioritized, or in-progress work.",
7
+ oneshot: false,
8
+ enabled: true,
9
+ metadata: {
10
+ heartbeatFile: opts?.heartbeatFile ?? ".tentickle/HEARTBEAT.md",
11
+ },
12
+ };
13
+ }
14
+ //# sourceMappingURL=heartbeat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../src/heartbeat.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAAC,IAAuB;IACxD,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,aAAa;QACjC,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK;QAC7B,MAAM,EACJ,8FAA8F;QAChG,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE;YACR,aAAa,EAAE,IAAI,EAAE,aAAa,IAAI,yBAAyB;SAChE;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ export { CronService } from "./cron-service.js";
2
+ export { JobStore } from "./job-store.js";
3
+ export { createNodeCronBackend } from "./node-cron-backend.js";
4
+ export { TriggerWatcher } from "./trigger-watcher.js";
5
+ export { createScheduleTool } from "./schedule-tool.js";
6
+ export { createHeartbeatJob } from "./heartbeat.js";
7
+ export { bindSchedulerStore, getSchedulerStore } from "./bridge.js";
8
+ export type { Job, Trigger, SchedulerBackend, CronServiceOptions, HeartbeatOptions, CreateJobInput, } from "./types.js";
9
+ export type { NodeCronBackendOptions } from "./node-cron-backend.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpE,YAAY,EACV,GAAG,EACH,OAAO,EACP,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { CronService } from "./cron-service.js";
2
+ export { JobStore } from "./job-store.js";
3
+ export { createNodeCronBackend } from "./node-cron-backend.js";
4
+ export { TriggerWatcher } from "./trigger-watcher.js";
5
+ export { createScheduleTool } from "./schedule-tool.js";
6
+ export { createHeartbeatJob } from "./heartbeat.js";
7
+ export { bindSchedulerStore, getSchedulerStore } from "./bridge.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { EventEmitter } from "node:events";
2
+ import type { Job, CreateJobInput } from "./types.js";
3
+ export declare class JobStore extends EventEmitter {
4
+ private jobs;
5
+ private readonly jobsDir;
6
+ constructor(jobsDir: string);
7
+ list(): Job[];
8
+ listEnabled(): Job[];
9
+ get(id: string): Job | null;
10
+ create(input: CreateJobInput): Job;
11
+ update(id: string, updates: Partial<Pick<Job, "name" | "cron" | "target" | "prompt" | "oneshot" | "enabled" | "lastFiredAt" | "metadata">>): Job | null;
12
+ delete(id: string): boolean;
13
+ private _generateId;
14
+ private _nanoid;
15
+ private _load;
16
+ private _save;
17
+ private _remove;
18
+ }
19
+ //# sourceMappingURL=job-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-store.d.ts","sourceRoot":"","sources":["../src/job-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAU3C,OAAO,KAAK,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAUtD,qBAAa,QAAS,SAAQ,YAAY;IACxC,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,MAAM;IAO3B,IAAI,IAAI,GAAG,EAAE;IAIb,WAAW,IAAI,GAAG,EAAE;IAIpB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAI3B,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,GAAG;IAiBlC,MAAM,CACJ,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,OAAO,CACd,IAAI,CACF,GAAG,EACH,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,UAAU,CAC3F,CACF,GACA,GAAG,GAAG,IAAI;IASb,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAS3B,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,KAAK;IAcb,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,OAAO;CAQhB"}
@@ -0,0 +1,113 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { mkdirSync, readdirSync, readFileSync, writeFileSync, unlinkSync, existsSync, } from "node:fs";
3
+ import { join } from "node:path";
4
+ function slugify(name) {
5
+ return name
6
+ .toLowerCase()
7
+ .replace(/[^a-z0-9]+/g, "-")
8
+ .replace(/^-|-$/g, "")
9
+ .slice(0, 48);
10
+ }
11
+ export class JobStore extends EventEmitter {
12
+ jobs = new Map();
13
+ jobsDir;
14
+ constructor(jobsDir) {
15
+ super();
16
+ this.jobsDir = jobsDir;
17
+ mkdirSync(jobsDir, { recursive: true });
18
+ this._load();
19
+ }
20
+ list() {
21
+ return [...this.jobs.values()];
22
+ }
23
+ listEnabled() {
24
+ return this.list().filter((j) => j.enabled);
25
+ }
26
+ get(id) {
27
+ return this.jobs.get(id) ?? null;
28
+ }
29
+ create(input) {
30
+ const id = input.id ?? this._generateId(input.name);
31
+ if (this.jobs.has(id)) {
32
+ throw new Error(`Job "${id}" already exists`);
33
+ }
34
+ const job = {
35
+ ...input,
36
+ id,
37
+ createdAt: new Date().toISOString(),
38
+ };
39
+ this.jobs.set(id, job);
40
+ this._save(job);
41
+ this.emit("change");
42
+ return job;
43
+ }
44
+ update(id, updates) {
45
+ const job = this.jobs.get(id);
46
+ if (!job)
47
+ return null;
48
+ Object.assign(job, updates);
49
+ this._save(job);
50
+ this.emit("change");
51
+ return job;
52
+ }
53
+ delete(id) {
54
+ const existed = this.jobs.delete(id);
55
+ if (existed) {
56
+ this._remove(id);
57
+ this.emit("change");
58
+ }
59
+ return existed;
60
+ }
61
+ _generateId(name) {
62
+ const base = slugify(name);
63
+ if (!base)
64
+ return this._nanoid();
65
+ if (!this.jobs.has(base))
66
+ return base;
67
+ for (let i = 2; i < 1000; i++) {
68
+ const candidate = `${base}-${i}`;
69
+ if (!this.jobs.has(candidate))
70
+ return candidate;
71
+ }
72
+ return `${base}-${this._nanoid()}`;
73
+ }
74
+ _nanoid() {
75
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
76
+ let id = "";
77
+ const bytes = new Uint8Array(8);
78
+ crypto.getRandomValues(bytes);
79
+ for (const b of bytes)
80
+ id += chars[b % chars.length];
81
+ return id;
82
+ }
83
+ _load() {
84
+ if (!existsSync(this.jobsDir))
85
+ return;
86
+ for (const file of readdirSync(this.jobsDir)) {
87
+ if (!file.endsWith(".json"))
88
+ continue;
89
+ try {
90
+ const raw = readFileSync(join(this.jobsDir, file), "utf-8");
91
+ const job = JSON.parse(raw);
92
+ if (job.id)
93
+ this.jobs.set(job.id, job);
94
+ }
95
+ catch {
96
+ // Skip malformed files
97
+ }
98
+ }
99
+ }
100
+ _save(job) {
101
+ writeFileSync(join(this.jobsDir, `${job.id}.json`), JSON.stringify(job, null, 2) + "\n");
102
+ }
103
+ _remove(id) {
104
+ const path = join(this.jobsDir, `${id}.json`);
105
+ try {
106
+ unlinkSync(path);
107
+ }
108
+ catch {
109
+ // Already gone
110
+ }
111
+ }
112
+ }
113
+ //# sourceMappingURL=job-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-store.js","sourceRoot":"","sources":["../src/job-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { SchedulerBackend } from "./types.js";
2
+ export interface NodeCronBackendOptions {
3
+ /** Directory for trigger files (crash recovery). If omitted, no trigger files — fires directly. */
4
+ triggersDir?: string;
5
+ }
6
+ /**
7
+ * In-process scheduling via node-cron. Optionally writes trigger files for
8
+ * crash recovery — if the process dies between fire and dispatch, trigger
9
+ * files persist and are drained on next `start()`.
10
+ */
11
+ export declare function createNodeCronBackend(options?: NodeCronBackendOptions): SchedulerBackend;
12
+ //# sourceMappingURL=node-cron-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-cron-backend.d.ts","sourceRoot":"","sources":["../src/node-cron-backend.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAgB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,sBAAsB;IACrC,mGAAmG;IACnG,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAQD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,gBAAgB,CAgDxF"}
@@ -0,0 +1,124 @@
1
+ import cron, {} from "node-cron";
2
+ import { writeFileSync, readFileSync, readdirSync, unlinkSync, existsSync, mkdirSync, } from "node:fs";
3
+ import { join } from "node:path";
4
+ /**
5
+ * In-process scheduling via node-cron. Optionally writes trigger files for
6
+ * crash recovery — if the process dies between fire and dispatch, trigger
7
+ * files persist and are drained on next `start()`.
8
+ */
9
+ export function createNodeCronBackend(options) {
10
+ const timers = new Map();
11
+ const triggersDir = options?.triggersDir;
12
+ const pendingCallbacks = new Map();
13
+ if (triggersDir) {
14
+ mkdirSync(triggersDir, { recursive: true });
15
+ }
16
+ return {
17
+ async start() {
18
+ if (triggersDir) {
19
+ await drainPendingTriggers(triggersDir, pendingCallbacks);
20
+ }
21
+ },
22
+ async stop() {
23
+ for (const [id, entry] of timers) {
24
+ entry.task.stop();
25
+ timers.delete(id);
26
+ }
27
+ },
28
+ schedule(job, onFire) {
29
+ if (!cron.validate(job.cron))
30
+ return;
31
+ // Store callback for crash recovery drain
32
+ pendingCallbacks.set(job.id, onFire);
33
+ const wrappedFire = triggersDir
34
+ ? () => fireWithTriggerFile(triggersDir, job, onFire)
35
+ : () => {
36
+ onFire();
37
+ };
38
+ const task = cron.schedule(job.cron, wrappedFire);
39
+ timers.set(job.id, { task, cronExpr: job.cron, onFire });
40
+ },
41
+ unschedule(jobId) {
42
+ const entry = timers.get(jobId);
43
+ if (entry) {
44
+ entry.task.stop();
45
+ timers.delete(jobId);
46
+ }
47
+ pendingCallbacks.delete(jobId);
48
+ },
49
+ };
50
+ }
51
+ /** Write trigger file, call onFire, delete file on success. */
52
+ function fireWithTriggerFile(triggersDir, job, onFire) {
53
+ // Heartbeat pre-filter: read heartbeat file, skip if empty/missing
54
+ let prompt = job.prompt;
55
+ const heartbeatFile = job.metadata?.heartbeatFile;
56
+ if (heartbeatFile) {
57
+ try {
58
+ if (!existsSync(heartbeatFile))
59
+ return;
60
+ const contents = readFileSync(heartbeatFile, "utf-8").trim();
61
+ if (!contents)
62
+ return;
63
+ prompt = `${prompt}\n\n---\n\n${contents}`;
64
+ }
65
+ catch {
66
+ return;
67
+ }
68
+ }
69
+ const now = new Date();
70
+ const trigger = {
71
+ jobId: job.id,
72
+ jobName: job.name,
73
+ target: job.target,
74
+ prompt,
75
+ firedAt: now.toISOString(),
76
+ oneshot: job.oneshot,
77
+ };
78
+ const filename = `${now.getTime()}-${job.id}.json`;
79
+ const filepath = join(triggersDir, filename);
80
+ writeFileSync(filepath, JSON.stringify(trigger, null, 2) + "\n");
81
+ // Fire callback, clean up trigger file on success
82
+ onFire()
83
+ .then(() => {
84
+ try {
85
+ unlinkSync(filepath);
86
+ }
87
+ catch { }
88
+ })
89
+ .catch(() => {
90
+ // Trigger file persists for retry on next start
91
+ });
92
+ }
93
+ /** Drain trigger files that survived a crash. */
94
+ async function drainPendingTriggers(triggersDir, callbacks) {
95
+ let files;
96
+ try {
97
+ files = readdirSync(triggersDir)
98
+ .filter((f) => f.endsWith(".json"))
99
+ .sort();
100
+ }
101
+ catch {
102
+ return;
103
+ }
104
+ for (const file of files) {
105
+ const filepath = join(triggersDir, file);
106
+ try {
107
+ const raw = readFileSync(filepath, "utf-8");
108
+ const trigger = JSON.parse(raw);
109
+ const callback = callbacks.get(trigger.jobId);
110
+ if (callback) {
111
+ await callback();
112
+ }
113
+ unlinkSync(filepath);
114
+ }
115
+ catch {
116
+ // Skip malformed trigger files
117
+ try {
118
+ unlinkSync(filepath);
119
+ }
120
+ catch { }
121
+ }
122
+ }
123
+ }
124
+ //# sourceMappingURL=node-cron-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-cron-backend.js","sourceRoot":"","sources":["../src/node-cron-backend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { JobStore } from "./job-store.js";
2
+ export declare function createScheduleTool(store: JobStore): import("@agentick/core/tool").RunnableToolClass<{
3
+ action: "add" | "list" | "remove" | "enable" | "disable";
4
+ name?: string | undefined;
5
+ cron?: string | undefined;
6
+ target?: string | undefined;
7
+ prompt?: string | undefined;
8
+ oneshot?: boolean | undefined;
9
+ id?: string | undefined;
10
+ }>;
11
+ //# sourceMappingURL=schedule-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-tool.d.ts","sourceRoot":"","sources":["../src/schedule-tool.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAe/C,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,QAAQ;;;;;;;;GAwFjD"}
@@ -0,0 +1,101 @@
1
+ import React from "react";
2
+ import cron from "node-cron";
3
+ import { createTool, Section } from "@agentick/core";
4
+ import { z } from "zod";
5
+ function formatJob(j) {
6
+ const flags = [j.oneshot ? "oneshot" : null, !j.enabled ? "disabled" : null].filter(Boolean);
7
+ const suffix = flags.length > 0 ? ` [${flags.join(", ")}]` : "";
8
+ return `${j.id}: "${j.name}" (${j.cron}) → ${j.target}${suffix}`;
9
+ }
10
+ export function createScheduleTool(store) {
11
+ return createTool({
12
+ name: "schedule",
13
+ description: "Manage scheduled jobs. 'add' to create a recurring/oneshot job, " +
14
+ "'list' to see all jobs, 'remove' to delete, 'enable'/'disable' to toggle.",
15
+ displaySummary: (input) => {
16
+ if (input.action === "add")
17
+ return `add: ${input.name ?? "unnamed"}`;
18
+ if (input.action === "list")
19
+ return "list";
20
+ return `${input.action}: ${input.id}`;
21
+ },
22
+ input: z.object({
23
+ action: z.enum(["add", "list", "remove", "enable", "disable"]),
24
+ name: z.string().optional().describe("Job name (add)"),
25
+ cron: z.string().optional().describe("Cron expression, e.g. '*/5 * * * *' (add)"),
26
+ target: z.string().optional().describe("Target session ID, e.g. 'tui', 'telegram' (add)"),
27
+ prompt: z.string().optional().describe("Prompt sent when job fires (add)"),
28
+ oneshot: z.boolean().optional().describe("Delete after first fire (add, default false)"),
29
+ id: z.string().optional().describe("Job ID (remove/enable/disable)"),
30
+ }),
31
+ handler: (input) => {
32
+ switch (input.action) {
33
+ case "add": {
34
+ if (!input.name || !input.cron || !input.prompt) {
35
+ return [
36
+ { type: "text", text: "Error: 'add' requires name, cron, and prompt." },
37
+ ];
38
+ }
39
+ if (!cron.validate(input.cron)) {
40
+ return [
41
+ { type: "text", text: `Error: invalid cron expression "${input.cron}".` },
42
+ ];
43
+ }
44
+ const job = store.create({
45
+ name: input.name,
46
+ cron: input.cron,
47
+ target: input.target ?? "tui",
48
+ prompt: input.prompt,
49
+ oneshot: input.oneshot ?? false,
50
+ enabled: true,
51
+ });
52
+ return [{ type: "text", text: `Created job: ${formatJob(job)}` }];
53
+ }
54
+ case "list": {
55
+ const jobs = store.list();
56
+ if (jobs.length === 0) {
57
+ return [{ type: "text", text: "No scheduled jobs." }];
58
+ }
59
+ return [{ type: "text", text: jobs.map(formatJob).join("\n") }];
60
+ }
61
+ case "remove": {
62
+ if (!input.id) {
63
+ return [{ type: "text", text: "Error: 'remove' requires an id." }];
64
+ }
65
+ const deleted = store.delete(input.id);
66
+ if (!deleted) {
67
+ return [{ type: "text", text: `Error: job "${input.id}" not found.` }];
68
+ }
69
+ return [{ type: "text", text: `Removed job "${input.id}".` }];
70
+ }
71
+ case "enable": {
72
+ if (!input.id) {
73
+ return [{ type: "text", text: "Error: 'enable' requires an id." }];
74
+ }
75
+ const job = store.update(input.id, { enabled: true });
76
+ if (!job) {
77
+ return [{ type: "text", text: `Error: job "${input.id}" not found.` }];
78
+ }
79
+ return [{ type: "text", text: `Enabled job "${input.id}".` }];
80
+ }
81
+ case "disable": {
82
+ if (!input.id) {
83
+ return [{ type: "text", text: "Error: 'disable' requires an id." }];
84
+ }
85
+ const job = store.update(input.id, { enabled: false });
86
+ if (!job) {
87
+ return [{ type: "text", text: `Error: job "${input.id}" not found.` }];
88
+ }
89
+ return [{ type: "text", text: `Disabled job "${input.id}".` }];
90
+ }
91
+ }
92
+ },
93
+ render: () => {
94
+ const jobs = store.listEnabled();
95
+ if (jobs.length === 0)
96
+ return null;
97
+ return <Section id="scheduled-jobs">{jobs.map(formatJob).join("\n")}</Section>;
98
+ },
99
+ });
100
+ }
101
+ //# sourceMappingURL=schedule-tool.jsx.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-tool.jsx","sourceRoot":"","sources":["../src/schedule-tool.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { AgentickClient } from "@agentick/client";
2
+ import type { CronServiceOptions } from "./types.js";
3
+ import type { JobStore } from "./job-store.js";
4
+ export declare class TriggerWatcher {
5
+ private watcher;
6
+ private readonly triggersDir;
7
+ private readonly client;
8
+ private readonly store;
9
+ private readonly defaultTarget;
10
+ private readonly onProcessed;
11
+ private readonly onError;
12
+ private processing;
13
+ private stopped;
14
+ constructor(triggersDir: string, client: AgentickClient, store: JobStore, options?: Pick<CronServiceOptions, "defaultTarget" | "onTriggerProcessed" | "onError">);
15
+ start(): Promise<void>;
16
+ stop(): void;
17
+ private _drainPending;
18
+ private _processFile;
19
+ }
20
+ //# sourceMappingURL=trigger-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trigger-watcher.d.ts","sourceRoot":"","sources":["../src/trigger-watcher.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAW,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAW;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2C;IACvE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwD;IAChF,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,OAAO,CAAS;gBAGtB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,QAAQ,EACf,OAAO,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,eAAe,GAAG,oBAAoB,GAAG,SAAS,CAAC;IAWlF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B,IAAI,IAAI,IAAI;YAQE,aAAa;YAgBb,YAAY;CAiE3B"}
@@ -0,0 +1,114 @@
1
+ import { watch, readdirSync, readFileSync, unlinkSync, existsSync, mkdirSync, } from "node:fs";
2
+ import { join } from "node:path";
3
+ export class TriggerWatcher {
4
+ watcher = null;
5
+ triggersDir;
6
+ client;
7
+ store;
8
+ defaultTarget;
9
+ onProcessed;
10
+ onError;
11
+ processing = new Set();
12
+ stopped = false;
13
+ constructor(triggersDir, client, store, options) {
14
+ this.triggersDir = triggersDir;
15
+ this.client = client;
16
+ this.store = store;
17
+ this.defaultTarget = options?.defaultTarget;
18
+ this.onProcessed = options?.onTriggerProcessed;
19
+ this.onError = options?.onError;
20
+ mkdirSync(triggersDir, { recursive: true });
21
+ }
22
+ async start() {
23
+ this.stopped = false;
24
+ // Drain any pending triggers from before process started
25
+ await this._drainPending();
26
+ // Watch for new triggers
27
+ this.watcher = watch(this.triggersDir, (eventType, filename) => {
28
+ if (this.stopped)
29
+ return;
30
+ if (eventType === "rename" && filename?.endsWith(".json")) {
31
+ this._processFile(filename);
32
+ }
33
+ });
34
+ }
35
+ stop() {
36
+ this.stopped = true;
37
+ if (this.watcher) {
38
+ this.watcher.close();
39
+ this.watcher = null;
40
+ }
41
+ }
42
+ async _drainPending() {
43
+ let files;
44
+ try {
45
+ files = readdirSync(this.triggersDir)
46
+ .filter((f) => f.endsWith(".json"))
47
+ .sort();
48
+ }
49
+ catch {
50
+ return;
51
+ }
52
+ for (const file of files) {
53
+ if (this.stopped)
54
+ break;
55
+ await this._processFile(file);
56
+ }
57
+ }
58
+ async _processFile(filename) {
59
+ // Deduplicate: fs.watch fires multiple events for the same file
60
+ if (this.processing.has(filename))
61
+ return;
62
+ this.processing.add(filename);
63
+ const filepath = join(this.triggersDir, filename);
64
+ try {
65
+ if (!existsSync(filepath))
66
+ return;
67
+ const raw = readFileSync(filepath, "utf-8");
68
+ const trigger = JSON.parse(raw);
69
+ if (!trigger.target && !this.defaultTarget) {
70
+ this.onError?.(new Error(`Trigger ${filename} has no target and no default configured`), "processFile");
71
+ return;
72
+ }
73
+ const target = trigger.target || this.defaultTarget;
74
+ // Send as an event-role message — this is a system event, not a user turn
75
+ const session = this.client.session(target);
76
+ const handle = session.send({
77
+ messages: [
78
+ {
79
+ role: "event",
80
+ content: [{ type: "text", text: trigger.prompt }],
81
+ metadata: {
82
+ source: { type: "cron" },
83
+ event_type: "cron_trigger",
84
+ job_id: trigger.jobId,
85
+ job_name: trigger.jobName,
86
+ fired_at: trigger.firedAt,
87
+ },
88
+ },
89
+ ],
90
+ });
91
+ // Wait for the model to process — don't delete trigger until delivery confirmed
92
+ await handle.result;
93
+ // Delete trigger file after successful delivery
94
+ try {
95
+ unlinkSync(filepath);
96
+ }
97
+ catch {
98
+ // Already cleaned up
99
+ }
100
+ // If oneshot, delete the job
101
+ if (trigger.oneshot) {
102
+ this.store.delete(trigger.jobId);
103
+ }
104
+ this.onProcessed?.(trigger);
105
+ }
106
+ catch (error) {
107
+ this.onError?.(error instanceof Error ? error : new Error(String(error)), `processFile:${filename}`);
108
+ }
109
+ finally {
110
+ this.processing.delete(filename);
111
+ }
112
+ }
113
+ }
114
+ //# sourceMappingURL=trigger-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trigger-watcher.js","sourceRoot":"","sources":["../src/trigger-watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EAEL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,UAAU,EACV,SAAS,GACV,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAKjC,MAAM,OAAO,cAAc;IACjB,OAAO,GAAqB,IAAI,CAAC;IACxB,WAAW,CAAS;IACpB,MAAM,CAAiB;IACvB,KAAK,CAAW;IAChB,aAAa,CAAqB;IAClC,WAAW,CAA2C;IACtD,OAAO,CAAwD;IACxE,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,OAAO,GAAG,KAAK,CAAC;IAExB,YACE,WAAmB,EACnB,MAAsB,EACtB,KAAe,EACf,OAAsF;QAEtF,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,aAAa,GAAG,OAAO,EAAE,aAAa,CAAC;QAC5C,IAAI,CAAC,WAAW,GAAG,OAAO,EAAE,kBAAkB,CAAC;QAC/C,IAAI,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,CAAC;QAChC,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,yDAAyD;QACzD,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAC3B,yBAAyB;QACzB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE;YAC7D,IAAI,IAAI,CAAC,OAAO;gBAAE,OAAO;YACzB,IAAI,SAAS,KAAK,QAAQ,IAAI,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1D,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI;QACF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,aAAa;QACzB,IAAI,KAAe,CAAC;QACpB,IAAI,CAAC;YACH,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC;iBAClC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;iBAClC,IAAI,EAAE,CAAC;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,OAAO;gBAAE,MAAM;YACxB,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,QAAgB;QACzC,gEAAgE;QAChE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO;QAC1C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAE9B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QAClD,IAAI,CAAC;YACH,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;gBAAE,OAAO;YAElC,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;YAE3C,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;gBAC3C,IAAI,CAAC,OAAO,EAAE,CACZ,IAAI,KAAK,CAAC,WAAW,QAAQ,0CAA0C,CAAC,EACxE,aAAa,CACd,CAAC;gBACF,OAAO;YACT,CAAC;YAED,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,aAAc,CAAC;YAErD,0EAA0E;YAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC5C,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;gBAC1B,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,OAAO;wBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;wBACjD,QAAQ,EAAE;4BACR,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;4BACxB,UAAU,EAAE,cAAc;4BAC1B,MAAM,EAAE,OAAO,CAAC,KAAK;4BACrB,QAAQ,EAAE,OAAO,CAAC,OAAO;4BACzB,QAAQ,EAAE,OAAO,CAAC,OAAO;yBAC1B;qBACF;iBACF;aACF,CAAC,CAAC;YAEH,gFAAgF;YAChF,MAAM,MAAM,CAAC,MAAM,CAAC;YAEpB,gDAAgD;YAChD,IAAI,CAAC;gBACH,UAAU,CAAC,QAAQ,CAAC,CAAC;YACvB,CAAC;YAAC,MAAM,CAAC;gBACP,qBAAqB;YACvB,CAAC;YAED,6BAA6B;YAC7B,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBACpB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC;YAED,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,OAAO,EAAE,CACZ,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EACzD,eAAe,QAAQ,EAAE,CAC1B,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,52 @@
1
+ import type { AgentickClient } from "@agentick/client";
2
+ declare module "@agentick/shared" {
3
+ interface MessageSourceTypes {
4
+ cron: {
5
+ type: "cron";
6
+ };
7
+ }
8
+ }
9
+ export interface Job {
10
+ id: string;
11
+ name: string;
12
+ cron: string;
13
+ target: string;
14
+ prompt: string;
15
+ oneshot: boolean;
16
+ enabled: boolean;
17
+ createdAt: string;
18
+ lastFiredAt?: string;
19
+ metadata?: Record<string, unknown>;
20
+ }
21
+ export interface Trigger {
22
+ jobId: string;
23
+ jobName: string;
24
+ target: string;
25
+ prompt: string;
26
+ firedAt: string;
27
+ oneshot: boolean;
28
+ }
29
+ export interface SchedulerBackend {
30
+ start(): Promise<void>;
31
+ stop(): Promise<void>;
32
+ schedule(job: Job, onFire: () => Promise<void>): void;
33
+ unschedule(jobId: string): void;
34
+ }
35
+ export interface CronServiceOptions {
36
+ dataDir: string;
37
+ client: AgentickClient;
38
+ backend?: SchedulerBackend;
39
+ watchExternalTriggers?: boolean;
40
+ defaultTarget?: string;
41
+ onTriggerProcessed?: (trigger: Trigger) => void;
42
+ onError?: (error: Error, context: string) => void;
43
+ }
44
+ export interface HeartbeatOptions {
45
+ cron?: string;
46
+ target?: string;
47
+ heartbeatFile?: string;
48
+ }
49
+ export type CreateJobInput = Omit<Job, "id" | "createdAt" | "lastFiredAt"> & {
50
+ id?: string;
51
+ };
52
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,QAAQ,kBAAkB,CAAC;IAChC,UAAU,kBAAkB;QAC1B,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;KACxB;CACF;AAED,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtD,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG;IAC3E,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@agentick/scheduler",
3
+ "version": "0.7.0",
4
+ "description": "Scheduled jobs, heartbeat, and cron triggers for Agentick agents",
5
+ "keywords": [
6
+ "agent",
7
+ "ai",
8
+ "cron",
9
+ "heartbeat",
10
+ "scheduler"
11
+ ],
12
+ "license": "MIT",
13
+ "author": "Ryan Lindgren",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/agenticklabs/agentick.git",
17
+ "directory": "packages/scheduler"
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "type": "module",
23
+ "main": "src/index.ts",
24
+ "exports": {
25
+ ".": "./src/index.ts"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.js"
33
+ }
34
+ },
35
+ "main": "./dist/index.js",
36
+ "types": "./dist/index.d.ts"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc -p tsconfig.build.json",
40
+ "test": "echo \"Tests run from workspace root\"",
41
+ "typecheck": "tsc -p tsconfig.build.json --noEmit",
42
+ "lint": "oxlint src/",
43
+ "format:check": "oxfmt --check src/",
44
+ "clean": "rm -rf dist tsconfig.build.tsbuildinfo",
45
+ "prepublishOnly": "pnpm build",
46
+ "dev": "tsc --watch"
47
+ },
48
+ "dependencies": {
49
+ "@agentick/client": "workspace:*",
50
+ "@agentick/core": "workspace:*",
51
+ "@agentick/shared": "workspace:*",
52
+ "node-cron": "^3.0.3",
53
+ "zod": "^4.3.6"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node-cron": "^3.0.11",
57
+ "@types/react": "^19.0.0",
58
+ "react": "^19.0.0",
59
+ "typescript": "^5.8.3"
60
+ },
61
+ "peerDependencies": {
62
+ "react": "^19.0.0"
63
+ }
64
+ }
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ export { CronService } from "./cron-service.js";
2
+ export { JobStore } from "./job-store.js";
3
+ export { createNodeCronBackend } from "./node-cron-backend.js";
4
+ export { TriggerWatcher } from "./trigger-watcher.js";
5
+ export { createScheduleTool } from "./schedule-tool.js";
6
+ export { createHeartbeatJob } from "./heartbeat.js";
7
+ export { bindSchedulerStore, getSchedulerStore } from "./bridge.js";
8
+ export type {
9
+ Job,
10
+ Trigger,
11
+ SchedulerBackend,
12
+ CronServiceOptions,
13
+ HeartbeatOptions,
14
+ CreateJobInput,
15
+ } from "./types.js";
16
+ export type { NodeCronBackendOptions } from "./node-cron-backend.js";