@zhin.js/agent 1.1.38 → 1.1.40
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/lib/assistant/job-engine.d.ts +5 -0
- package/lib/assistant/job-engine.js +20 -1
- package/lib/assistant/job-engine.js.map +1 -1
- package/lib/assistant/job-storage.d.ts +238 -0
- package/lib/assistant/job-storage.js +143 -0
- package/lib/assistant/job-storage.js.map +1 -0
- package/lib/assistant/job-store.d.ts +7 -0
- package/lib/assistant/job-store.js +41 -3
- package/lib/assistant/job-store.js.map +1 -1
- package/lib/config/perception-config.d.ts +9 -0
- package/lib/config/perception-config.js +27 -0
- package/lib/config/perception-config.js.map +1 -1
- package/lib/context/context-system.d.ts +1 -0
- package/lib/context/context-system.js +8 -8
- package/lib/context/context-system.js.map +1 -1
- package/lib/context/im-turn-context-adapter.js +2 -0
- package/lib/context/im-turn-context-adapter.js.map +1 -1
- package/lib/context/index.d.ts +1 -1
- package/lib/context/index.js +1 -1
- package/lib/context/index.js.map +1 -1
- package/lib/context/skill-context.d.ts +3 -0
- package/lib/context/skill-context.js +6 -0
- package/lib/context/skill-context.js.map +1 -0
- package/lib/context/turn-envelope.d.ts +7 -1
- package/lib/context/turn-envelope.js +30 -7
- package/lib/context/turn-envelope.js.map +1 -1
- package/lib/context/turn-user-message.d.ts +3 -1
- package/lib/context/turn-user-message.js +14 -1
- package/lib/context/turn-user-message.js.map +1 -1
- package/lib/core/agent-core-run.d.ts +1 -0
- package/lib/core/agent-core-run.js +68 -34
- package/lib/core/agent-core-run.js.map +1 -1
- package/lib/core/structured-reply-recovery.d.ts +13 -0
- package/lib/core/structured-reply-recovery.js +41 -5
- package/lib/core/structured-reply-recovery.js.map +1 -1
- package/lib/core/turn-completion-usage.d.ts +2 -1
- package/lib/core/turn-completion-usage.js +14 -5
- package/lib/core/turn-completion-usage.js.map +1 -1
- package/lib/core/turn-event-mapper.d.ts +1 -1
- package/lib/core/turn-event-mapper.js +8 -5
- package/lib/core/turn-event-mapper.js.map +1 -1
- package/lib/event/turn-event.d.ts +7 -1
- package/lib/event/turn-event.js.map +1 -1
- package/lib/index.d.ts +4 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/media/perception.d.ts +3 -4
- package/lib/media/perception.js +59 -37
- package/lib/media/perception.js.map +1 -1
- package/lib/memory/compaction-runtime.d.ts +3 -2
- package/lib/memory/compaction-runtime.js +22 -14
- package/lib/memory/compaction-runtime.js.map +1 -1
- package/lib/outbound/agent-output.js +2 -0
- package/lib/outbound/agent-output.js.map +1 -1
- package/lib/outbound/chat-failure.d.ts +27 -0
- package/lib/outbound/chat-failure.js +53 -0
- package/lib/outbound/chat-failure.js.map +1 -0
- package/lib/outbound/structured-reply.d.ts +7 -0
- package/lib/outbound/structured-reply.js +72 -16
- package/lib/outbound/structured-reply.js.map +1 -1
- package/lib/plugin-runtime/agent-runtime.js +49 -8
- package/lib/plugin-runtime/agent-runtime.js.map +1 -1
- package/lib/plugin-runtime/ai-completion.d.ts +4 -3
- package/lib/plugin-runtime/ai-completion.js +64 -10
- package/lib/plugin-runtime/ai-completion.js.map +1 -1
- package/lib/plugin-runtime/capability-ingress.js +3 -1
- package/lib/plugin-runtime/capability-ingress.js.map +1 -1
- package/lib/plugin-runtime/deferred-capability-plan.d.ts +4 -1
- package/lib/plugin-runtime/deferred-capability-plan.js +25 -9
- package/lib/plugin-runtime/deferred-capability-plan.js.map +1 -1
- package/lib/plugin-runtime/full-agent-turn-engine.js +1 -0
- package/lib/plugin-runtime/full-agent-turn-engine.js.map +1 -1
- package/lib/plugin-runtime/index.d.ts +2 -0
- package/lib/plugin-runtime/index.js +2 -0
- package/lib/plugin-runtime/index.js.map +1 -1
- package/lib/prompt/assembly.js +0 -9
- package/lib/prompt/assembly.js.map +1 -1
- package/lib/prompt/git-context.js +1 -1
- package/lib/prompt/git-context.js.map +1 -1
- package/lib/prompt/rules/chat-rules.js +8 -9
- package/lib/prompt/rules/chat-rules.js.map +1 -1
- package/lib/prompt/rules/critical-rules.js +10 -10
- package/lib/prompt/rules/critical-rules.js.map +1 -1
- package/lib/prompt/system-prompt.js +4 -5
- package/lib/prompt/system-prompt.js.map +1 -1
- package/lib/security/file-role-policy.js +6 -6
- package/lib/security/file-role-policy.js.map +1 -1
- package/lib/security/policy-facade.js +1 -1
- package/lib/security/policy-facade.js.map +1 -1
- package/lib/security/usage-quota.d.ts +4 -0
- package/lib/security/usage-quota.js +23 -2
- package/lib/security/usage-quota.js.map +1 -1
- package/lib/session/approval-port.d.ts +2 -0
- package/lib/session/approval-port.js.map +1 -1
- package/lib/session/approval-review-agent.d.ts +1 -1
- package/lib/session/approval-review-agent.js +19 -4
- package/lib/session/approval-review-agent.js.map +1 -1
- package/lib/session/turn-ingress-session.js +7 -1
- package/lib/session/turn-ingress-session.js.map +1 -1
- package/lib/tool/tool-approval-gate.js +4 -2
- package/lib/tool/tool-approval-gate.js.map +1 -1
- package/lib/tool/turn-tool-runtime.js +24 -5
- package/lib/tool/turn-tool-runtime.js.map +1 -1
- package/lib/tool-catalog/deferred-turn-controller.js +1 -4
- package/lib/tool-catalog/deferred-turn-controller.js.map +1 -1
- package/lib/trace/context-budget.d.ts +4 -3
- package/lib/trace/context-budget.js +28 -8
- package/lib/trace/context-budget.js.map +1 -1
- package/lib/trace/turn-trace-format.js +4 -1
- package/lib/trace/turn-trace-format.js.map +1 -1
- package/lib/trace/turn-trace.d.ts +6 -0
- package/lib/trace/turn-trace.js +8 -2
- package/lib/trace/turn-trace.js.map +1 -1
- package/lib/trace/turn-usage-log.d.ts +14 -0
- package/lib/trace/turn-usage-log.js +83 -0
- package/lib/trace/turn-usage-log.js.map +1 -0
- package/lib/usage/usage-store.d.ts +199 -0
- package/lib/usage/usage-store.js +121 -0
- package/lib/usage/usage-store.js.map +1 -0
- package/package.json +13 -13
|
@@ -8,6 +8,8 @@ export interface ScheduleJobEngineOptions {
|
|
|
8
8
|
notifyOnFailure?: boolean;
|
|
9
9
|
router: NotificationRouter;
|
|
10
10
|
defaultNotify?: import('./types.js').JobNotify;
|
|
11
|
+
/** Workspace holding HEARTBEAT.md; a default-prompt heartbeat is skipped while that file has no task. */
|
|
12
|
+
workspaceDir?: string;
|
|
11
13
|
}
|
|
12
14
|
export declare class ScheduleJobEngine {
|
|
13
15
|
#private;
|
|
@@ -17,12 +19,15 @@ export declare class ScheduleJobEngine {
|
|
|
17
19
|
private notifyOnFailure;
|
|
18
20
|
private router;
|
|
19
21
|
private defaultNotify?;
|
|
22
|
+
private workspaceDir?;
|
|
20
23
|
constructor(options: ScheduleJobEngineOptions);
|
|
21
24
|
getDataDir(): string;
|
|
22
25
|
load(): Promise<void>;
|
|
23
26
|
registerOne(job: ScheduleJob): void;
|
|
24
27
|
runJobNow(jobId: string): Promise<void>;
|
|
25
28
|
private runJob;
|
|
29
|
+
/** The default heartbeat prompt only reads HEARTBEAT.md: without a task there, a model turn is wasted. */
|
|
30
|
+
private isIdleHeartbeat;
|
|
26
31
|
listJobs(): Promise<ScheduleJob[]>;
|
|
27
32
|
addJob(job: Omit<ScheduleJob, 'createdAt' | 'updatedAt' | 'state'> & {
|
|
28
33
|
createdAt?: number;
|
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
* ScheduleJobEngine — schedule-jobs.json 持久化调度
|
|
3
3
|
*/
|
|
4
4
|
import { getLogger, formatCompact } from '@zhin.js/logger';
|
|
5
|
-
import {
|
|
5
|
+
import { readFile } from 'node:fs/promises';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { ScheduleEngine, isHeartbeatEmpty } from '@zhin.js/kernel';
|
|
6
8
|
import { registerJobSchedule, isRuntimeSchedulable } from './job-scheduler.js';
|
|
9
|
+
import { jobPrompt } from './job-utils.js';
|
|
10
|
+
import { DEFAULT_HEARTBEAT_PROMPT } from './profile-types.js';
|
|
7
11
|
import { resolveEffectiveNotify } from './notification-router.js';
|
|
8
12
|
const logger = getLogger('schedule-job-engine');
|
|
9
13
|
export class ScheduleJobEngine {
|
|
@@ -14,12 +18,14 @@ export class ScheduleJobEngine {
|
|
|
14
18
|
notifyOnFailure;
|
|
15
19
|
router;
|
|
16
20
|
defaultNotify;
|
|
21
|
+
workspaceDir;
|
|
17
22
|
constructor(options) {
|
|
18
23
|
this.store = options.store;
|
|
19
24
|
this.worker = options.worker;
|
|
20
25
|
this.notifyOnFailure = options.notifyOnFailure === true;
|
|
21
26
|
this.router = options.router;
|
|
22
27
|
this.defaultNotify = options.defaultNotify;
|
|
28
|
+
this.workspaceDir = options.workspaceDir;
|
|
23
29
|
}
|
|
24
30
|
getDataDir() {
|
|
25
31
|
return this.store.getDataDir();
|
|
@@ -51,6 +57,10 @@ export class ScheduleJobEngine {
|
|
|
51
57
|
return;
|
|
52
58
|
if (job.action.kind !== 'agent' && job.action.kind !== 'heartbeat')
|
|
53
59
|
return;
|
|
60
|
+
if (await this.isIdleHeartbeat(job)) {
|
|
61
|
+
logger.debug(formatCompact({ op: 'heartbeat_skipped', job: jobId, reason: 'no_heartbeat_tasks' }));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
54
64
|
const result = await this.worker.run(job);
|
|
55
65
|
await this.store.updateJobState(jobId, {
|
|
56
66
|
lastExecutedAt: Date.now(),
|
|
@@ -68,6 +78,15 @@ export class ScheduleJobEngine {
|
|
|
68
78
|
await this.removeJob(jobId);
|
|
69
79
|
}
|
|
70
80
|
}
|
|
81
|
+
/** The default heartbeat prompt only reads HEARTBEAT.md: without a task there, a model turn is wasted. */
|
|
82
|
+
async isIdleHeartbeat(job) {
|
|
83
|
+
if (job.action.kind !== 'heartbeat' || !this.workspaceDir)
|
|
84
|
+
return false;
|
|
85
|
+
if (jobPrompt(job).trim() !== DEFAULT_HEARTBEAT_PROMPT)
|
|
86
|
+
return false;
|
|
87
|
+
const content = await readFile(join(this.workspaceDir, 'HEARTBEAT.md'), 'utf8').catch(() => null);
|
|
88
|
+
return isHeartbeatEmpty(content);
|
|
89
|
+
}
|
|
71
90
|
async listJobs() {
|
|
72
91
|
return this.store.listJobs();
|
|
73
92
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-engine.js","sourceRoot":"","sources":["../../src/assistant/job-engine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ScheduleJobEngine — schedule-jobs.json 持久化调度\n */\nimport { getLogger, formatCompact } from '@zhin.js/logger';\nimport { ScheduleEngine } from '@zhin.js/kernel';\nimport { registerJobSchedule, isRuntimeSchedulable } from './job-scheduler.js';\nimport type { ScheduleJobStore } from './job-store.js';\nimport type { JobWorker } from './job-worker.js';\nimport type { ScheduleJob } from './types.js';\nimport { type NotificationRouter, resolveEffectiveNotify } from './notification-router.js';\nconst logger = getLogger('schedule-job-engine');\n\nexport interface ScheduleJobEngineOptions {\n store: ScheduleJobStore;\n worker: JobWorker;\n notifyOnFailure?: boolean;\n router: NotificationRouter;\n defaultNotify?: import('./types.js').JobNotify;\n}\n\nexport class ScheduleJobEngine {\n readonly #scheduler = new ScheduleEngine();\n private store: ScheduleJobStore;\n private worker: JobWorker;\n private disposes = new Map<string, () => void>();\n private notifyOnFailure: boolean;\n private router: NotificationRouter;\n private defaultNotify?: import('./types.js').JobNotify;\n\n constructor(options: ScheduleJobEngineOptions) {\n this.store = options.store;\n this.worker = options.worker;\n this.notifyOnFailure = options.notifyOnFailure === true;\n this.router = options.router;\n this.defaultNotify = options.defaultNotify;\n }\n\n getDataDir(): string {\n return this.store.getDataDir();\n }\n\n async load(): Promise<void> {\n const jobs = await this.store.listJobs();\n for (const job of jobs) {\n if (isRuntimeSchedulable(job)) {\n this.registerOne(job);\n }\n }\n const count = jobs.filter((j) => isRuntimeSchedulable(j)).length;\n if (count > 0) {\n logger.debug(formatCompact({ schedule_jobs: count }));\n }\n }\n\n registerOne(job: ScheduleJob): void {\n const dispose = registerJobSchedule(this.#scheduler, job, (jobId) => this.runJob(jobId));\n if (dispose) {\n this.disposes.set(job.id, dispose);\n }\n }\n\n async runJobNow(jobId: string): Promise<void> {\n await this.runJob(jobId);\n }\n\n private async runJob(jobId: string): Promise<void> {\n const job = await this.store.getJob(jobId);\n if (!job || !job.enabled) return;\n if (job.action.kind !== 'agent' && job.action.kind !== 'heartbeat') return;\n\n const result = await this.worker.run(job);\n\n await this.store.updateJobState(jobId, {\n lastExecutedAt: Date.now(),\n lastStatus: result.success ? 'ok' : 'error',\n lastError: result.success ? undefined : result.error,\n });\n\n if (!result.success && (job.notifyOnFailure ?? this.notifyOnFailure)) {\n const notify = resolveEffectiveNotify(job.notify, this.defaultNotify);\n if (notify.channel !== 'silent' && notify.channel !== 'log') {\n const msg = `[任务失败] ${job.label || jobId}: ${result.error || 'unknown error'}`;\n await this.router.deliver({ notify, content: msg, jobId, label: job.label }).catch(() => {});\n }\n }\n\n if (result.success && job.schedule.kind === 'at' && job.schedule.deleteAfterRun) {\n await this.removeJob(jobId);\n }\n }\n\n async listJobs(): Promise<ScheduleJob[]> {\n return this.store.listJobs();\n }\n\n async addJob(job: Omit<ScheduleJob, 'createdAt' | 'updatedAt' | 'state'> & {\n createdAt?: number;\n updatedAt?: number;\n state?: ScheduleJob['state'];\n }): Promise<ScheduleJob> {\n const now = Date.now();\n const full: ScheduleJob = {\n ...job,\n createdAt: job.createdAt ?? now,\n updatedAt: job.updatedAt ?? now,\n state: job.state ?? {},\n enabled: job.enabled ?? true,\n };\n full.source = job.source ?? 'manual';\n await this.store.upsertJob(full);\n if (isRuntimeSchedulable(full)) {\n this.registerOne(full);\n }\n return full;\n }\n\n async removeJob(id: string): Promise<boolean> {\n const ok = await this.store.removeJob(id);\n if (!ok) return false;\n const dispose = this.disposes.get(id);\n if (dispose) {\n dispose();\n this.disposes.delete(id);\n }\n return true;\n }\n\n async pauseJob(id: string): Promise<boolean> {\n const job = await this.store.getJob(id);\n if (!job) return false;\n job.enabled = false;\n await this.store.upsertJob(job);\n const dispose = this.disposes.get(id);\n if (dispose) {\n dispose();\n this.disposes.delete(id);\n }\n return true;\n }\n\n async resumeJob(id: string): Promise<boolean> {\n const job = await this.store.getJob(id);\n if (!job) return false;\n job.enabled = true;\n await this.store.upsertJob(job);\n if (isRuntimeSchedulable(job)) {\n this.registerOne(job);\n }\n return true;\n }\n\n unload(): void {\n for (const [id, dispose] of this.disposes) {\n try {\n dispose();\n } catch (e) {\n logger.warn(`Schedule dispose failed for ${id}:`, e);\n }\n }\n this.disposes.clear();\n this.#scheduler.dispose();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"job-engine.js","sourceRoot":"","sources":["../../src/assistant/job-engine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ScheduleJobEngine — schedule-jobs.json 持久化调度\n */\nimport { getLogger, formatCompact } from '@zhin.js/logger';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { ScheduleEngine, isHeartbeatEmpty } from '@zhin.js/kernel';\nimport { registerJobSchedule, isRuntimeSchedulable } from './job-scheduler.js';\nimport type { ScheduleJobStore } from './job-store.js';\nimport type { JobWorker } from './job-worker.js';\nimport type { ScheduleJob } from './types.js';\nimport { jobPrompt } from './job-utils.js';\nimport { DEFAULT_HEARTBEAT_PROMPT } from './profile-types.js';\nimport { type NotificationRouter, resolveEffectiveNotify } from './notification-router.js';\nconst logger = getLogger('schedule-job-engine');\n\nexport interface ScheduleJobEngineOptions {\n store: ScheduleJobStore;\n worker: JobWorker;\n notifyOnFailure?: boolean;\n router: NotificationRouter;\n defaultNotify?: import('./types.js').JobNotify;\n /** Workspace holding HEARTBEAT.md; a default-prompt heartbeat is skipped while that file has no task. */\n workspaceDir?: string;\n}\n\nexport class ScheduleJobEngine {\n readonly #scheduler = new ScheduleEngine();\n private store: ScheduleJobStore;\n private worker: JobWorker;\n private disposes = new Map<string, () => void>();\n private notifyOnFailure: boolean;\n private router: NotificationRouter;\n private defaultNotify?: import('./types.js').JobNotify;\n private workspaceDir?: string;\n\n constructor(options: ScheduleJobEngineOptions) {\n this.store = options.store;\n this.worker = options.worker;\n this.notifyOnFailure = options.notifyOnFailure === true;\n this.router = options.router;\n this.defaultNotify = options.defaultNotify;\n this.workspaceDir = options.workspaceDir;\n }\n\n getDataDir(): string {\n return this.store.getDataDir();\n }\n\n async load(): Promise<void> {\n const jobs = await this.store.listJobs();\n for (const job of jobs) {\n if (isRuntimeSchedulable(job)) {\n this.registerOne(job);\n }\n }\n const count = jobs.filter((j) => isRuntimeSchedulable(j)).length;\n if (count > 0) {\n logger.debug(formatCompact({ schedule_jobs: count }));\n }\n }\n\n registerOne(job: ScheduleJob): void {\n const dispose = registerJobSchedule(this.#scheduler, job, (jobId) => this.runJob(jobId));\n if (dispose) {\n this.disposes.set(job.id, dispose);\n }\n }\n\n async runJobNow(jobId: string): Promise<void> {\n await this.runJob(jobId);\n }\n\n private async runJob(jobId: string): Promise<void> {\n const job = await this.store.getJob(jobId);\n if (!job || !job.enabled) return;\n if (job.action.kind !== 'agent' && job.action.kind !== 'heartbeat') return;\n if (await this.isIdleHeartbeat(job)) {\n logger.debug(formatCompact({ op: 'heartbeat_skipped', job: jobId, reason: 'no_heartbeat_tasks' }));\n return;\n }\n\n const result = await this.worker.run(job);\n\n await this.store.updateJobState(jobId, {\n lastExecutedAt: Date.now(),\n lastStatus: result.success ? 'ok' : 'error',\n lastError: result.success ? undefined : result.error,\n });\n\n if (!result.success && (job.notifyOnFailure ?? this.notifyOnFailure)) {\n const notify = resolveEffectiveNotify(job.notify, this.defaultNotify);\n if (notify.channel !== 'silent' && notify.channel !== 'log') {\n const msg = `[任务失败] ${job.label || jobId}: ${result.error || 'unknown error'}`;\n await this.router.deliver({ notify, content: msg, jobId, label: job.label }).catch(() => {});\n }\n }\n\n if (result.success && job.schedule.kind === 'at' && job.schedule.deleteAfterRun) {\n await this.removeJob(jobId);\n }\n }\n\n /** The default heartbeat prompt only reads HEARTBEAT.md: without a task there, a model turn is wasted. */\n private async isIdleHeartbeat(job: ScheduleJob): Promise<boolean> {\n if (job.action.kind !== 'heartbeat' || !this.workspaceDir) return false;\n if (jobPrompt(job).trim() !== DEFAULT_HEARTBEAT_PROMPT) return false;\n const content = await readFile(join(this.workspaceDir, 'HEARTBEAT.md'), 'utf8').catch(() => null);\n return isHeartbeatEmpty(content);\n }\n\n async listJobs(): Promise<ScheduleJob[]> {\n return this.store.listJobs();\n }\n\n async addJob(job: Omit<ScheduleJob, 'createdAt' | 'updatedAt' | 'state'> & {\n createdAt?: number;\n updatedAt?: number;\n state?: ScheduleJob['state'];\n }): Promise<ScheduleJob> {\n const now = Date.now();\n const full: ScheduleJob = {\n ...job,\n createdAt: job.createdAt ?? now,\n updatedAt: job.updatedAt ?? now,\n state: job.state ?? {},\n enabled: job.enabled ?? true,\n };\n full.source = job.source ?? 'manual';\n await this.store.upsertJob(full);\n if (isRuntimeSchedulable(full)) {\n this.registerOne(full);\n }\n return full;\n }\n\n async removeJob(id: string): Promise<boolean> {\n const ok = await this.store.removeJob(id);\n if (!ok) return false;\n const dispose = this.disposes.get(id);\n if (dispose) {\n dispose();\n this.disposes.delete(id);\n }\n return true;\n }\n\n async pauseJob(id: string): Promise<boolean> {\n const job = await this.store.getJob(id);\n if (!job) return false;\n job.enabled = false;\n await this.store.upsertJob(job);\n const dispose = this.disposes.get(id);\n if (dispose) {\n dispose();\n this.disposes.delete(id);\n }\n return true;\n }\n\n async resumeJob(id: string): Promise<boolean> {\n const job = await this.store.getJob(id);\n if (!job) return false;\n job.enabled = true;\n await this.store.upsertJob(job);\n if (isRuntimeSchedulable(job)) {\n this.registerOne(job);\n }\n return true;\n }\n\n unload(): void {\n for (const [id, dispose] of this.disposes) {\n try {\n dispose();\n } catch (e) {\n logger.warn(`Schedule dispose failed for ${id}:`, e);\n }\n }\n this.disposes.clear();\n this.#scheduler.dispose();\n }\n}\n"]}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import type { DatabaseHost } from '@zhin.js/plugin-runtime';
|
|
2
|
+
import type { ScheduleJob, ScheduleJobFile } from './types.js';
|
|
3
|
+
export declare const SCHEDULE_JOB_MODEL: {
|
|
4
|
+
id: {
|
|
5
|
+
type: string;
|
|
6
|
+
length: number;
|
|
7
|
+
primary: boolean;
|
|
8
|
+
};
|
|
9
|
+
job_id: {
|
|
10
|
+
type: string;
|
|
11
|
+
nullable: boolean;
|
|
12
|
+
};
|
|
13
|
+
position: {
|
|
14
|
+
type: string;
|
|
15
|
+
nullable: boolean;
|
|
16
|
+
};
|
|
17
|
+
label: {
|
|
18
|
+
readonly type: "text";
|
|
19
|
+
readonly nullable: true;
|
|
20
|
+
};
|
|
21
|
+
enabled: {
|
|
22
|
+
readonly type: "boolean";
|
|
23
|
+
readonly nullable: true;
|
|
24
|
+
};
|
|
25
|
+
created_at: {
|
|
26
|
+
readonly type: "bigint";
|
|
27
|
+
readonly nullable: true;
|
|
28
|
+
};
|
|
29
|
+
updated_at: {
|
|
30
|
+
readonly type: "bigint";
|
|
31
|
+
readonly nullable: true;
|
|
32
|
+
};
|
|
33
|
+
source: {
|
|
34
|
+
readonly type: "text";
|
|
35
|
+
readonly nullable: true;
|
|
36
|
+
};
|
|
37
|
+
creator_id: {
|
|
38
|
+
readonly type: "text";
|
|
39
|
+
readonly nullable: true;
|
|
40
|
+
};
|
|
41
|
+
creator_name: {
|
|
42
|
+
readonly type: "text";
|
|
43
|
+
readonly nullable: true;
|
|
44
|
+
};
|
|
45
|
+
creator_roles_json: {
|
|
46
|
+
readonly type: "text";
|
|
47
|
+
readonly nullable: true;
|
|
48
|
+
};
|
|
49
|
+
schedule_kind: {
|
|
50
|
+
readonly type: "text";
|
|
51
|
+
readonly nullable: true;
|
|
52
|
+
};
|
|
53
|
+
cron: {
|
|
54
|
+
readonly type: "text";
|
|
55
|
+
readonly nullable: true;
|
|
56
|
+
};
|
|
57
|
+
timezone: {
|
|
58
|
+
readonly type: "text";
|
|
59
|
+
readonly nullable: true;
|
|
60
|
+
};
|
|
61
|
+
every_ms: {
|
|
62
|
+
readonly type: "bigint";
|
|
63
|
+
readonly nullable: true;
|
|
64
|
+
};
|
|
65
|
+
at_ms: {
|
|
66
|
+
readonly type: "bigint";
|
|
67
|
+
readonly nullable: true;
|
|
68
|
+
};
|
|
69
|
+
delete_after_run: {
|
|
70
|
+
readonly type: "boolean";
|
|
71
|
+
readonly nullable: true;
|
|
72
|
+
};
|
|
73
|
+
event_id: {
|
|
74
|
+
readonly type: "text";
|
|
75
|
+
readonly nullable: true;
|
|
76
|
+
};
|
|
77
|
+
event_source: {
|
|
78
|
+
readonly type: "text";
|
|
79
|
+
readonly nullable: true;
|
|
80
|
+
};
|
|
81
|
+
event_type: {
|
|
82
|
+
readonly type: "text";
|
|
83
|
+
readonly nullable: true;
|
|
84
|
+
};
|
|
85
|
+
schedule_options_json: {
|
|
86
|
+
readonly type: "text";
|
|
87
|
+
readonly nullable: true;
|
|
88
|
+
};
|
|
89
|
+
action_kind: {
|
|
90
|
+
readonly type: "text";
|
|
91
|
+
readonly nullable: true;
|
|
92
|
+
};
|
|
93
|
+
prompt: {
|
|
94
|
+
readonly type: "text";
|
|
95
|
+
readonly nullable: true;
|
|
96
|
+
};
|
|
97
|
+
agent: {
|
|
98
|
+
readonly type: "text";
|
|
99
|
+
readonly nullable: true;
|
|
100
|
+
};
|
|
101
|
+
notify_channel: {
|
|
102
|
+
readonly type: "text";
|
|
103
|
+
readonly nullable: true;
|
|
104
|
+
};
|
|
105
|
+
platform: {
|
|
106
|
+
readonly type: "text";
|
|
107
|
+
readonly nullable: true;
|
|
108
|
+
};
|
|
109
|
+
endpoint_id: {
|
|
110
|
+
readonly type: "text";
|
|
111
|
+
readonly nullable: true;
|
|
112
|
+
};
|
|
113
|
+
conversation_kind: {
|
|
114
|
+
readonly type: "text";
|
|
115
|
+
readonly nullable: true;
|
|
116
|
+
};
|
|
117
|
+
conversation_id: {
|
|
118
|
+
readonly type: "text";
|
|
119
|
+
readonly nullable: true;
|
|
120
|
+
};
|
|
121
|
+
sender_id: {
|
|
122
|
+
readonly type: "text";
|
|
123
|
+
readonly nullable: true;
|
|
124
|
+
};
|
|
125
|
+
parent_kind: {
|
|
126
|
+
readonly type: "text";
|
|
127
|
+
readonly nullable: true;
|
|
128
|
+
};
|
|
129
|
+
parent_id: {
|
|
130
|
+
readonly type: "text";
|
|
131
|
+
readonly nullable: true;
|
|
132
|
+
};
|
|
133
|
+
thread_id: {
|
|
134
|
+
readonly type: "text";
|
|
135
|
+
readonly nullable: true;
|
|
136
|
+
};
|
|
137
|
+
quote_id: {
|
|
138
|
+
readonly type: "text";
|
|
139
|
+
readonly nullable: true;
|
|
140
|
+
};
|
|
141
|
+
ha_service: {
|
|
142
|
+
readonly type: "text";
|
|
143
|
+
readonly nullable: true;
|
|
144
|
+
};
|
|
145
|
+
ha_target: {
|
|
146
|
+
readonly type: "text";
|
|
147
|
+
readonly nullable: true;
|
|
148
|
+
};
|
|
149
|
+
ha_data_json: {
|
|
150
|
+
readonly type: "text";
|
|
151
|
+
readonly nullable: true;
|
|
152
|
+
};
|
|
153
|
+
notify_on_failure: {
|
|
154
|
+
readonly type: "boolean";
|
|
155
|
+
readonly nullable: true;
|
|
156
|
+
};
|
|
157
|
+
activity_feedback: {
|
|
158
|
+
readonly type: "boolean";
|
|
159
|
+
readonly nullable: true;
|
|
160
|
+
};
|
|
161
|
+
last_executed_at: {
|
|
162
|
+
readonly type: "bigint";
|
|
163
|
+
readonly nullable: true;
|
|
164
|
+
};
|
|
165
|
+
last_status: {
|
|
166
|
+
readonly type: "text";
|
|
167
|
+
readonly nullable: true;
|
|
168
|
+
};
|
|
169
|
+
last_error: {
|
|
170
|
+
readonly type: "text";
|
|
171
|
+
readonly nullable: true;
|
|
172
|
+
};
|
|
173
|
+
next_run_at: {
|
|
174
|
+
readonly type: "bigint";
|
|
175
|
+
readonly nullable: true;
|
|
176
|
+
};
|
|
177
|
+
retry_count: {
|
|
178
|
+
type: string;
|
|
179
|
+
nullable: boolean;
|
|
180
|
+
};
|
|
181
|
+
plan_prompt: {
|
|
182
|
+
readonly type: "text";
|
|
183
|
+
readonly nullable: true;
|
|
184
|
+
};
|
|
185
|
+
plan_tools_json: {
|
|
186
|
+
readonly type: "text";
|
|
187
|
+
readonly nullable: true;
|
|
188
|
+
};
|
|
189
|
+
plan_skills_json: {
|
|
190
|
+
readonly type: "text";
|
|
191
|
+
readonly nullable: true;
|
|
192
|
+
};
|
|
193
|
+
plan_preview: {
|
|
194
|
+
readonly type: "text";
|
|
195
|
+
readonly nullable: true;
|
|
196
|
+
};
|
|
197
|
+
plan_previewed_at: {
|
|
198
|
+
readonly type: "bigint";
|
|
199
|
+
readonly nullable: true;
|
|
200
|
+
};
|
|
201
|
+
plan_confirmed: {
|
|
202
|
+
readonly type: "boolean";
|
|
203
|
+
readonly nullable: true;
|
|
204
|
+
};
|
|
205
|
+
budget_json: {
|
|
206
|
+
readonly type: "text";
|
|
207
|
+
readonly nullable: true;
|
|
208
|
+
};
|
|
209
|
+
event_payload_json: {
|
|
210
|
+
readonly type: "text";
|
|
211
|
+
readonly nullable: true;
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
export declare const SCHEDULE_JOB_MIGRATION_MODEL: {
|
|
215
|
+
id: {
|
|
216
|
+
type: string;
|
|
217
|
+
primary: boolean;
|
|
218
|
+
};
|
|
219
|
+
version: {
|
|
220
|
+
type: string;
|
|
221
|
+
nullable: boolean;
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
export declare function encodeScheduleJob(job: ScheduleJob, position: number): Record<string, unknown>;
|
|
225
|
+
export declare function decodeScheduleJob(row: Record<string, unknown>): ScheduleJob;
|
|
226
|
+
/** Existing worker stays unchanged; mutations and first import use one DB transaction. */
|
|
227
|
+
export declare class DatabaseScheduleJobStorage {
|
|
228
|
+
private readonly database;
|
|
229
|
+
constructor(database: DatabaseHost);
|
|
230
|
+
activate(loadLegacy: () => Promise<ScheduleJobFile>): Promise<void>;
|
|
231
|
+
read(): Promise<ScheduleJobFile>;
|
|
232
|
+
get(id: string): Promise<ScheduleJob | undefined>;
|
|
233
|
+
mutateJob<T>(id: string, mutate: (job: ScheduleJob | undefined) => {
|
|
234
|
+
job: ScheduleJob | undefined;
|
|
235
|
+
result: T;
|
|
236
|
+
}): Promise<T>;
|
|
237
|
+
update<T>(mutate: (file: ScheduleJobFile) => T): Promise<T>;
|
|
238
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { encodeStorageIdentity, decodeStorageIdentity, retryRelationalOperation } from '@zhin.js/plugin-runtime';
|
|
3
|
+
const text = { type: 'text', nullable: true };
|
|
4
|
+
const time = { type: 'bigint', nullable: true };
|
|
5
|
+
const flag = { type: 'boolean', nullable: true };
|
|
6
|
+
export const SCHEDULE_JOB_MODEL = {
|
|
7
|
+
id: { type: 'text', length: 64, primary: true }, job_id: { type: 'text', nullable: false }, position: { type: 'integer', nullable: false }, label: text, enabled: flag,
|
|
8
|
+
created_at: time, updated_at: time, source: text, creator_id: text, creator_name: text, creator_roles_json: text,
|
|
9
|
+
schedule_kind: text, cron: text, timezone: text, every_ms: time, at_ms: time, delete_after_run: flag,
|
|
10
|
+
event_id: text, event_source: text, event_type: text, schedule_options_json: text,
|
|
11
|
+
action_kind: text, prompt: text, agent: text,
|
|
12
|
+
notify_channel: text, platform: text, endpoint_id: text, conversation_kind: text, conversation_id: text, sender_id: text,
|
|
13
|
+
parent_kind: text, parent_id: text, thread_id: text, quote_id: text, ha_service: text, ha_target: text, ha_data_json: text,
|
|
14
|
+
notify_on_failure: flag, activity_feedback: flag,
|
|
15
|
+
last_executed_at: time, last_status: text, last_error: text, next_run_at: time, retry_count: { type: 'integer', nullable: true },
|
|
16
|
+
plan_prompt: text, plan_tools_json: text, plan_skills_json: text, plan_preview: text, plan_previewed_at: time, plan_confirmed: flag,
|
|
17
|
+
budget_json: text, event_payload_json: text,
|
|
18
|
+
};
|
|
19
|
+
export const SCHEDULE_JOB_MIGRATION_MODEL = { id: { type: 'integer', primary: true }, version: { type: 'integer', nullable: false } };
|
|
20
|
+
const jobKey = (id) => createHash('sha256').update(id).digest('hex');
|
|
21
|
+
const json = (value) => value === undefined ? null : JSON.stringify(value);
|
|
22
|
+
const parse = (value) => value == null ? undefined : JSON.parse(String(value));
|
|
23
|
+
const encode = (value) => value === undefined ? null : encodeStorageIdentity(value);
|
|
24
|
+
const decode = (value) => value == null ? undefined : decodeStorageIdentity(value);
|
|
25
|
+
export function encodeScheduleJob(job, position) {
|
|
26
|
+
const schedule = job.schedule;
|
|
27
|
+
const scene = job.notify.channel === 'im' ? job.notify.target.scene : undefined;
|
|
28
|
+
const target = job.notify.channel === 'im' ? job.notify.target : undefined;
|
|
29
|
+
return { id: jobKey(job.id), job_id: encodeStorageIdentity(job.id), position, label: encode(job.label), enabled: job.enabled,
|
|
30
|
+
created_at: job.createdAt, updated_at: job.updatedAt, source: job.source ?? null,
|
|
31
|
+
creator_id: encode(job.createdBy?.userId), creator_name: encode(job.createdBy?.name), creator_roles_json: json(job.createdBy?.roles),
|
|
32
|
+
schedule_kind: schedule.kind, cron: 'cron' in schedule ? schedule.cron : null, timezone: 'tz' in schedule ? schedule.tz ?? null : null,
|
|
33
|
+
every_ms: schedule.kind === 'every' ? schedule.everyMs : null, at_ms: schedule.kind === 'at' ? schedule.atMs : null,
|
|
34
|
+
delete_after_run: schedule.kind === 'at' ? schedule.deleteAfterRun ?? null : null,
|
|
35
|
+
event_id: schedule.kind === 'event' ? encode(schedule.eventId) : null,
|
|
36
|
+
event_source: schedule.kind === 'event' ? encode(schedule.source) : null, event_type: schedule.kind === 'event' ? encode(schedule.eventType) : null,
|
|
37
|
+
schedule_options_json: schedule.kind === 'scatter' ? json(schedule.input) : schedule.kind === 'holiday' ? json({ festivals: schedule.festivals, everyDayOfHoliday: schedule.everyDayOfHoliday }) : null,
|
|
38
|
+
action_kind: job.action.kind, prompt: encode(job.action.prompt), agent: job.action.kind === 'agent' ? encode(job.action.agent) : null,
|
|
39
|
+
notify_channel: job.notify.channel, platform: encode(scene?.platform), endpoint_id: encode(scene?.endpointKey), conversation_kind: scene?.kind ?? null,
|
|
40
|
+
conversation_id: encode(scene?.sceneId), sender_id: encode(scene?.senderId), parent_kind: scene?.parent?.kind ?? null, parent_id: encode(scene?.parent?.sceneId),
|
|
41
|
+
thread_id: encode(target?.threadId), quote_id: encode(target?.quoteId),
|
|
42
|
+
ha_service: job.notify.channel === 'ha' ? encode(job.notify.service) : null, ha_target: job.notify.channel === 'ha' ? encode(job.notify.target) : null,
|
|
43
|
+
ha_data_json: job.notify.channel === 'ha' ? json(job.notify.data) : null,
|
|
44
|
+
notify_on_failure: job.notifyOnFailure ?? null, activity_feedback: job.activityFeedback ?? null,
|
|
45
|
+
last_executed_at: job.state.lastExecutedAt ?? null, last_status: job.state.lastStatus ?? null, last_error: encode(job.state.lastError),
|
|
46
|
+
next_run_at: job.state.nextRunAtMs ?? null, retry_count: job.state.retryCount ?? null,
|
|
47
|
+
plan_prompt: encode(job.executionPlan?.prompt), plan_tools_json: json(job.executionPlan?.tools), plan_skills_json: json(job.executionPlan?.skills),
|
|
48
|
+
plan_preview: encode(job.executionPlan?.previewSample), plan_previewed_at: job.executionPlan?.previewedAt ?? null, plan_confirmed: job.executionPlan?.confirmed ?? null,
|
|
49
|
+
budget_json: json(job.budget), event_payload_json: json(job.eventPayload),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function decodeScheduleJob(row) {
|
|
53
|
+
const optional = (key, value) => value === undefined || value === null ? {} : { [key]: value };
|
|
54
|
+
const number = (value) => value == null ? undefined : Number(value);
|
|
55
|
+
const boolean = (value) => value == null ? undefined : Boolean(value);
|
|
56
|
+
const schedule = { kind: row.schedule_kind,
|
|
57
|
+
...optional('cron', row.cron), ...optional('tz', row.timezone), ...optional('everyMs', number(row.every_ms)), ...optional('atMs', number(row.at_ms)),
|
|
58
|
+
...optional('deleteAfterRun', boolean(row.delete_after_run)), ...optional('eventId', decode(row.event_id)), ...optional('source', decode(row.event_source)), ...optional('eventType', decode(row.event_type)),
|
|
59
|
+
...(row.schedule_kind === 'scatter' ? { input: parse(row.schedule_options_json) } : row.schedule_kind === 'holiday' ? parse(row.schedule_options_json) : {}),
|
|
60
|
+
};
|
|
61
|
+
const notify = row.notify_channel === 'im' ? { channel: 'im', target: { channel: 'im', scene: {
|
|
62
|
+
platform: decode(row.platform), endpointKey: decode(row.endpoint_id), sceneId: decode(row.conversation_id), kind: row.conversation_kind,
|
|
63
|
+
...optional('senderId', decode(row.sender_id)), ...(row.parent_kind == null ? {} : { parent: { kind: row.parent_kind, sceneId: decode(row.parent_id) } }),
|
|
64
|
+
}, ...optional('threadId', decode(row.thread_id)), ...optional('quoteId', decode(row.quote_id)) } }
|
|
65
|
+
: row.notify_channel === 'ha' ? { channel: 'ha', service: decode(row.ha_service), ...optional('target', decode(row.ha_target)), ...optional('data', parse(row.ha_data_json)) }
|
|
66
|
+
: { channel: row.notify_channel };
|
|
67
|
+
return { id: decodeStorageIdentity(row.job_id), ...optional('label', decode(row.label)), enabled: Boolean(row.enabled),
|
|
68
|
+
createdAt: Number(row.created_at), updatedAt: Number(row.updated_at), ...optional('source', row.source), schedule,
|
|
69
|
+
action: { kind: row.action_kind, prompt: decode(row.prompt), ...optional('agent', decode(row.agent)) }, notify,
|
|
70
|
+
...optional('notifyOnFailure', boolean(row.notify_on_failure)), ...optional('activityFeedback', boolean(row.activity_feedback)),
|
|
71
|
+
...(row.creator_id == null ? {} : { createdBy: { userId: decode(row.creator_id), roles: parse(row.creator_roles_json), ...optional('name', decode(row.creator_name)) } }),
|
|
72
|
+
state: { ...optional('lastExecutedAt', number(row.last_executed_at)), ...optional('lastStatus', row.last_status), ...optional('lastError', decode(row.last_error)),
|
|
73
|
+
...optional('nextRunAtMs', number(row.next_run_at)), ...optional('retryCount', number(row.retry_count)) },
|
|
74
|
+
...(row.plan_prompt == null ? {} : { executionPlan: { prompt: decode(row.plan_prompt), ...optional('tools', parse(row.plan_tools_json)), ...optional('skills', parse(row.plan_skills_json)),
|
|
75
|
+
...optional('previewSample', decode(row.plan_preview)), ...optional('previewedAt', number(row.plan_previewed_at)), ...optional('confirmed', boolean(row.plan_confirmed)) } }),
|
|
76
|
+
...optional('budget', parse(row.budget_json)), ...optional('eventPayload', parse(row.event_payload_json)),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** Existing worker stays unchanged; mutations and first import use one DB transaction. */
|
|
80
|
+
export class DatabaseScheduleJobStorage {
|
|
81
|
+
database;
|
|
82
|
+
constructor(database) {
|
|
83
|
+
this.database = database;
|
|
84
|
+
}
|
|
85
|
+
async activate(loadLegacy) {
|
|
86
|
+
await retryRelationalOperation(() => this.database.transaction(async (transaction) => {
|
|
87
|
+
const marker = transaction.model('agent_schedule_migration');
|
|
88
|
+
if ((await marker.select().where({ id: 1 }))[0])
|
|
89
|
+
return;
|
|
90
|
+
const file = await loadLegacy();
|
|
91
|
+
if (file.version !== 1)
|
|
92
|
+
throw new Error('Unsupported schedule-jobs file version');
|
|
93
|
+
for (const [position, job] of file.jobs.entries())
|
|
94
|
+
await transaction.model('agent_schedule_jobs').insert(encodeScheduleJob(job, position));
|
|
95
|
+
await marker.insert({ id: 1, version: file.version });
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
async read() {
|
|
99
|
+
const rows = await this.database.models.get('agent_schedule_jobs').select().orderBy('position');
|
|
100
|
+
return { version: 1, jobs: rows.map(decodeScheduleJob) };
|
|
101
|
+
}
|
|
102
|
+
async get(id) {
|
|
103
|
+
const row = (await this.database.models.get('agent_schedule_jobs').select().where({ id: jobKey(id) }).limit(1))[0];
|
|
104
|
+
return row ? decodeScheduleJob(row) : undefined;
|
|
105
|
+
}
|
|
106
|
+
async mutateJob(id, mutate) {
|
|
107
|
+
return retryRelationalOperation(() => this.database.transaction(async (transaction) => {
|
|
108
|
+
await transaction.model('agent_schedule_migration').update({ version: 1 }).where({ id: 1 });
|
|
109
|
+
const model = transaction.model('agent_schedule_jobs'), key = jobKey(id);
|
|
110
|
+
const existing = (await model.select().where({ id: key }).limit(1))[0];
|
|
111
|
+
const current = existing ? decodeScheduleJob(existing) : undefined;
|
|
112
|
+
const { job, result } = mutate(current);
|
|
113
|
+
if (job === current)
|
|
114
|
+
return result;
|
|
115
|
+
if (!job) {
|
|
116
|
+
await model.delete().where({ id: key });
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
if (existing)
|
|
120
|
+
await model.update(encodeScheduleJob(job, Number(existing.position))).where({ id: key });
|
|
121
|
+
else {
|
|
122
|
+
const last = (await model.select('position').orderBy('position', 'DESC').limit(1))[0];
|
|
123
|
+
await model.insert(encodeScheduleJob(job, last ? Number(last.position) + 1 : 0));
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
async update(mutate) {
|
|
129
|
+
return retryRelationalOperation(() => this.database.transaction(async (transaction) => {
|
|
130
|
+
// Acquire a write lock before reading; separate Host processes cannot lose updates.
|
|
131
|
+
await transaction.model('agent_schedule_migration').update({ version: 1 }).where({ id: 1 });
|
|
132
|
+
const model = transaction.model('agent_schedule_jobs');
|
|
133
|
+
const rows = await model.select().orderBy('position');
|
|
134
|
+
const file = { version: 1, jobs: rows.map(decodeScheduleJob) };
|
|
135
|
+
const result = mutate(file);
|
|
136
|
+
await model.delete().where({});
|
|
137
|
+
for (const [position, job] of file.jobs.entries())
|
|
138
|
+
await model.insert(encodeScheduleJob(job, position));
|
|
139
|
+
return result;
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=job-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-storage.js","sourceRoot":"","sources":["../../src/assistant/job-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport type { DatabaseHost } from '@zhin.js/plugin-runtime';\nimport { encodeStorageIdentity, decodeStorageIdentity, retryRelationalOperation } from '@zhin.js/plugin-runtime';\nimport type { ScheduleJob, ScheduleJobFile } from './types.js';\n\nconst text = { type: 'text', nullable: true } as const;\nconst time = { type: 'bigint', nullable: true } as const;\nconst flag = { type: 'boolean', nullable: true } as const;\nexport const SCHEDULE_JOB_MODEL = {\n id: { type: 'text', length: 64, primary: true }, job_id: { type: 'text', nullable: false }, position: { type: 'integer', nullable: false }, label: text, enabled: flag,\n created_at: time, updated_at: time, source: text, creator_id: text, creator_name: text, creator_roles_json: text,\n schedule_kind: text, cron: text, timezone: text, every_ms: time, at_ms: time, delete_after_run: flag,\n event_id: text, event_source: text, event_type: text, schedule_options_json: text,\n action_kind: text, prompt: text, agent: text,\n notify_channel: text, platform: text, endpoint_id: text, conversation_kind: text, conversation_id: text, sender_id: text,\n parent_kind: text, parent_id: text, thread_id: text, quote_id: text, ha_service: text, ha_target: text, ha_data_json: text,\n notify_on_failure: flag, activity_feedback: flag,\n last_executed_at: time, last_status: text, last_error: text, next_run_at: time, retry_count: { type: 'integer', nullable: true },\n plan_prompt: text, plan_tools_json: text, plan_skills_json: text, plan_preview: text, plan_previewed_at: time, plan_confirmed: flag,\n budget_json: text, event_payload_json: text,\n};\nexport const SCHEDULE_JOB_MIGRATION_MODEL = { id: { type: 'integer', primary: true }, version: { type: 'integer', nullable: false } };\nconst jobKey = (id: string) => createHash('sha256').update(id).digest('hex');\nconst json = (value: unknown) => value === undefined ? null : JSON.stringify(value);\nconst parse = (value: unknown) => value == null ? undefined : JSON.parse(String(value));\nconst encode = (value: string | undefined) => value === undefined ? null : encodeStorageIdentity(value);\nconst decode = (value: unknown) => value == null ? undefined : decodeStorageIdentity(value);\n\nexport function encodeScheduleJob(job: ScheduleJob, position: number): Record<string, unknown> {\n const schedule = job.schedule;\n const scene = job.notify.channel === 'im' ? job.notify.target.scene : undefined;\n const target = job.notify.channel === 'im' ? job.notify.target : undefined;\n return { id: jobKey(job.id), job_id: encodeStorageIdentity(job.id), position, label: encode(job.label), enabled: job.enabled,\n created_at: job.createdAt, updated_at: job.updatedAt, source: job.source ?? null,\n creator_id: encode(job.createdBy?.userId), creator_name: encode(job.createdBy?.name), creator_roles_json: json(job.createdBy?.roles),\n schedule_kind: schedule.kind, cron: 'cron' in schedule ? schedule.cron : null, timezone: 'tz' in schedule ? schedule.tz ?? null : null,\n every_ms: schedule.kind === 'every' ? schedule.everyMs : null, at_ms: schedule.kind === 'at' ? schedule.atMs : null,\n delete_after_run: schedule.kind === 'at' ? schedule.deleteAfterRun ?? null : null,\n event_id: schedule.kind === 'event' ? encode(schedule.eventId) : null,\n event_source: schedule.kind === 'event' ? encode(schedule.source) : null, event_type: schedule.kind === 'event' ? encode(schedule.eventType) : null,\n schedule_options_json: schedule.kind === 'scatter' ? json(schedule.input) : schedule.kind === 'holiday' ? json({ festivals: schedule.festivals, everyDayOfHoliday: schedule.everyDayOfHoliday }) : null,\n action_kind: job.action.kind, prompt: encode(job.action.prompt), agent: job.action.kind === 'agent' ? encode(job.action.agent) : null,\n notify_channel: job.notify.channel, platform: encode(scene?.platform), endpoint_id: encode(scene?.endpointKey), conversation_kind: scene?.kind ?? null,\n conversation_id: encode(scene?.sceneId), sender_id: encode(scene?.senderId), parent_kind: scene?.parent?.kind ?? null, parent_id: encode(scene?.parent?.sceneId),\n thread_id: encode(target?.threadId), quote_id: encode(target?.quoteId),\n ha_service: job.notify.channel === 'ha' ? encode(job.notify.service) : null, ha_target: job.notify.channel === 'ha' ? encode(job.notify.target) : null,\n ha_data_json: job.notify.channel === 'ha' ? json(job.notify.data) : null,\n notify_on_failure: job.notifyOnFailure ?? null, activity_feedback: job.activityFeedback ?? null,\n last_executed_at: job.state.lastExecutedAt ?? null, last_status: job.state.lastStatus ?? null, last_error: encode(job.state.lastError),\n next_run_at: job.state.nextRunAtMs ?? null, retry_count: job.state.retryCount ?? null,\n plan_prompt: encode(job.executionPlan?.prompt), plan_tools_json: json(job.executionPlan?.tools), plan_skills_json: json(job.executionPlan?.skills),\n plan_preview: encode(job.executionPlan?.previewSample), plan_previewed_at: job.executionPlan?.previewedAt ?? null, plan_confirmed: job.executionPlan?.confirmed ?? null,\n budget_json: json(job.budget), event_payload_json: json(job.eventPayload),\n };\n}\n\nexport function decodeScheduleJob(row: Record<string, unknown>): ScheduleJob {\n const optional = (key: string, value: unknown) => value === undefined || value === null ? {} : { [key]: value };\n const number = (value: unknown) => value == null ? undefined : Number(value);\n const boolean = (value: unknown) => value == null ? undefined : Boolean(value);\n const schedule = { kind: row.schedule_kind,\n ...optional('cron', row.cron), ...optional('tz', row.timezone), ...optional('everyMs', number(row.every_ms)), ...optional('atMs', number(row.at_ms)),\n ...optional('deleteAfterRun', boolean(row.delete_after_run)), ...optional('eventId', decode(row.event_id)), ...optional('source', decode(row.event_source)), ...optional('eventType', decode(row.event_type)),\n ...(row.schedule_kind === 'scatter' ? { input: parse(row.schedule_options_json) } : row.schedule_kind === 'holiday' ? parse(row.schedule_options_json) : {}),\n } as ScheduleJob['schedule'];\n const notify = row.notify_channel === 'im' ? { channel: 'im', target: { channel: 'im', scene: {\n platform: decode(row.platform), endpointKey: decode(row.endpoint_id), sceneId: decode(row.conversation_id), kind: row.conversation_kind,\n ...optional('senderId', decode(row.sender_id)), ...(row.parent_kind == null ? {} : { parent: { kind: row.parent_kind, sceneId: decode(row.parent_id) } }),\n }, ...optional('threadId', decode(row.thread_id)), ...optional('quoteId', decode(row.quote_id)) } }\n : row.notify_channel === 'ha' ? { channel: 'ha', service: decode(row.ha_service), ...optional('target', decode(row.ha_target)), ...optional('data', parse(row.ha_data_json)) }\n : { channel: row.notify_channel };\n return { id: decodeStorageIdentity(row.job_id), ...optional('label', decode(row.label)), enabled: Boolean(row.enabled),\n createdAt: Number(row.created_at), updatedAt: Number(row.updated_at), ...optional('source', row.source), schedule,\n action: { kind: row.action_kind, prompt: decode(row.prompt), ...optional('agent', decode(row.agent)) }, notify,\n ...optional('notifyOnFailure', boolean(row.notify_on_failure)), ...optional('activityFeedback', boolean(row.activity_feedback)),\n ...(row.creator_id == null ? {} : { createdBy: { userId: decode(row.creator_id), roles: parse(row.creator_roles_json), ...optional('name', decode(row.creator_name)) } }),\n state: { ...optional('lastExecutedAt', number(row.last_executed_at)), ...optional('lastStatus', row.last_status), ...optional('lastError', decode(row.last_error)),\n ...optional('nextRunAtMs', number(row.next_run_at)), ...optional('retryCount', number(row.retry_count)) },\n ...(row.plan_prompt == null ? {} : { executionPlan: { prompt: decode(row.plan_prompt), ...optional('tools', parse(row.plan_tools_json)), ...optional('skills', parse(row.plan_skills_json)),\n ...optional('previewSample', decode(row.plan_preview)), ...optional('previewedAt', number(row.plan_previewed_at)), ...optional('confirmed', boolean(row.plan_confirmed)) } }),\n ...optional('budget', parse(row.budget_json)), ...optional('eventPayload', parse(row.event_payload_json)),\n } as ScheduleJob;\n}\n\n/** Existing worker stays unchanged; mutations and first import use one DB transaction. */\nexport class DatabaseScheduleJobStorage {\n constructor(private readonly database: DatabaseHost) {}\n async activate(loadLegacy: () => Promise<ScheduleJobFile>): Promise<void> {\n await retryRelationalOperation(() => this.database.transaction!(async transaction => {\n const marker = transaction.model('agent_schedule_migration');\n if ((await marker.select().where({ id: 1 }))[0]) return;\n const file = await loadLegacy();\n if (file.version !== 1) throw new Error('Unsupported schedule-jobs file version');\n for (const [position, job] of file.jobs.entries()) await transaction.model('agent_schedule_jobs').insert(encodeScheduleJob(job, position));\n await marker.insert({ id: 1, version: file.version });\n }));\n }\n async read(): Promise<ScheduleJobFile> {\n const rows = await this.database.models.get('agent_schedule_jobs')!.select().orderBy('position');\n return { version: 1, jobs: rows.map(decodeScheduleJob) };\n }\n async get(id: string): Promise<ScheduleJob | undefined> {\n const row = (await this.database.models.get('agent_schedule_jobs')!.select().where({ id: jobKey(id) }).limit(1))[0];\n return row ? decodeScheduleJob(row) : undefined;\n }\n async mutateJob<T>(id: string, mutate: (job: ScheduleJob | undefined) => { job: ScheduleJob | undefined; result: T }): Promise<T> {\n return retryRelationalOperation(() => this.database.transaction!(async transaction => {\n await transaction.model('agent_schedule_migration').update({ version: 1 }).where({ id: 1 });\n const model = transaction.model('agent_schedule_jobs'), key = jobKey(id);\n const existing = (await model.select().where({ id: key }).limit(1))[0];\n const current = existing ? decodeScheduleJob(existing) : undefined;\n const { job, result } = mutate(current);\n if (job === current) return result;\n if (!job) { await model.delete().where({ id: key }); return result; }\n if (existing) await model.update(encodeScheduleJob(job, Number(existing.position))).where({ id: key });\n else {\n const last = (await model.select('position').orderBy('position', 'DESC').limit(1))[0];\n await model.insert(encodeScheduleJob(job, last ? Number(last.position) + 1 : 0));\n }\n return result;\n }));\n }\n async update<T>(mutate: (file: ScheduleJobFile) => T): Promise<T> {\n return retryRelationalOperation(() => this.database.transaction!(async transaction => {\n // Acquire a write lock before reading; separate Host processes cannot lose updates.\n await transaction.model('agent_schedule_migration').update({ version: 1 }).where({ id: 1 });\n const model = transaction.model('agent_schedule_jobs');\n const rows = await model.select().orderBy('position');\n const file = { version: 1, jobs: rows.map(decodeScheduleJob) };\n const result = mutate(file);\n await model.delete().where({});\n for (const [position, job] of file.jobs.entries()) await model.insert(encodeScheduleJob(job, position));\n return result;\n }));\n }\n}\n"]}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schedule JobStore — SQL persistence with the existing file mode as fallback
|
|
3
|
+
*/
|
|
4
|
+
import type { DatabaseScheduleJobStorage } from './job-storage.js';
|
|
1
5
|
import { type ScheduleJob, type ScheduleJobFile, type JobAction, type JobNotify } from './types.js';
|
|
2
6
|
export interface ScheduleJobStoreOptions {
|
|
3
7
|
dataDir: string;
|
|
@@ -10,6 +14,9 @@ export declare class ScheduleJobStore {
|
|
|
10
14
|
private filePath;
|
|
11
15
|
private defaultNotify?;
|
|
12
16
|
private cache;
|
|
17
|
+
private database?;
|
|
18
|
+
/** Must run before loading the existing scheduler or publishing this generation. */
|
|
19
|
+
activateDatabase(database: DatabaseScheduleJobStorage): Promise<void>;
|
|
13
20
|
constructor(options: ScheduleJobStoreOptions);
|
|
14
21
|
getDataDir(): string;
|
|
15
22
|
private emptyStore;
|