@zhin.js/agent 1.1.39 → 1.1.41

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.
Files changed (120) hide show
  1. package/lib/assistant/job-storage.d.ts +238 -0
  2. package/lib/assistant/job-storage.js +143 -0
  3. package/lib/assistant/job-storage.js.map +1 -0
  4. package/lib/assistant/job-store.d.ts +7 -0
  5. package/lib/assistant/job-store.js +41 -3
  6. package/lib/assistant/job-store.js.map +1 -1
  7. package/lib/config/perception-config.d.ts +9 -0
  8. package/lib/config/perception-config.js +27 -0
  9. package/lib/config/perception-config.js.map +1 -1
  10. package/lib/context/context-system.d.ts +1 -0
  11. package/lib/context/context-system.js +8 -8
  12. package/lib/context/context-system.js.map +1 -1
  13. package/lib/context/im-turn-context-adapter.js +2 -0
  14. package/lib/context/im-turn-context-adapter.js.map +1 -1
  15. package/lib/context/skill-context.d.ts +3 -0
  16. package/lib/context/skill-context.js +6 -0
  17. package/lib/context/skill-context.js.map +1 -0
  18. package/lib/context/turn-envelope.d.ts +4 -0
  19. package/lib/context/turn-envelope.js +24 -7
  20. package/lib/context/turn-envelope.js.map +1 -1
  21. package/lib/context/turn-user-message.d.ts +3 -1
  22. package/lib/context/turn-user-message.js +14 -1
  23. package/lib/context/turn-user-message.js.map +1 -1
  24. package/lib/core/agent-core-run.d.ts +1 -0
  25. package/lib/core/agent-core-run.js +47 -20
  26. package/lib/core/agent-core-run.js.map +1 -1
  27. package/lib/core/structured-reply-recovery.d.ts +6 -2
  28. package/lib/core/structured-reply-recovery.js +22 -8
  29. package/lib/core/structured-reply-recovery.js.map +1 -1
  30. package/lib/core/turn-completion-usage.d.ts +2 -1
  31. package/lib/core/turn-completion-usage.js +14 -5
  32. package/lib/core/turn-completion-usage.js.map +1 -1
  33. package/lib/core/turn-event-mapper.d.ts +1 -1
  34. package/lib/core/turn-event-mapper.js +8 -5
  35. package/lib/core/turn-event-mapper.js.map +1 -1
  36. package/lib/decision/config.d.ts +1 -1
  37. package/lib/decision/config.js +1 -1
  38. package/lib/decision/config.js.map +1 -1
  39. package/lib/decision/types.d.ts +1 -1
  40. package/lib/decision/types.js.map +1 -1
  41. package/lib/event/turn-event.d.ts +7 -1
  42. package/lib/event/turn-event.js.map +1 -1
  43. package/lib/index.d.ts +4 -1
  44. package/lib/index.js +2 -0
  45. package/lib/index.js.map +1 -1
  46. package/lib/media/perception.d.ts +3 -4
  47. package/lib/media/perception.js +59 -37
  48. package/lib/media/perception.js.map +1 -1
  49. package/lib/memory/compaction-runtime.d.ts +3 -2
  50. package/lib/memory/compaction-runtime.js +22 -14
  51. package/lib/memory/compaction-runtime.js.map +1 -1
  52. package/lib/outbound/agent-output.js +2 -0
  53. package/lib/outbound/agent-output.js.map +1 -1
  54. package/lib/outbound/chat-failure.d.ts +27 -0
  55. package/lib/outbound/chat-failure.js +53 -0
  56. package/lib/outbound/chat-failure.js.map +1 -0
  57. package/lib/outbound/structured-reply.d.ts +7 -0
  58. package/lib/outbound/structured-reply.js +52 -16
  59. package/lib/outbound/structured-reply.js.map +1 -1
  60. package/lib/plugin-runtime/agent-runtime.js +49 -8
  61. package/lib/plugin-runtime/agent-runtime.js.map +1 -1
  62. package/lib/plugin-runtime/ai-completion.d.ts +4 -3
  63. package/lib/plugin-runtime/ai-completion.js +64 -10
  64. package/lib/plugin-runtime/ai-completion.js.map +1 -1
  65. package/lib/plugin-runtime/capability-ingress.js +3 -1
  66. package/lib/plugin-runtime/capability-ingress.js.map +1 -1
  67. package/lib/plugin-runtime/decision-runtime.d.ts +1 -0
  68. package/lib/plugin-runtime/deferred-capability-plan.d.ts +4 -1
  69. package/lib/plugin-runtime/deferred-capability-plan.js +25 -9
  70. package/lib/plugin-runtime/deferred-capability-plan.js.map +1 -1
  71. package/lib/plugin-runtime/full-agent-turn-engine.js +1 -0
  72. package/lib/plugin-runtime/full-agent-turn-engine.js.map +1 -1
  73. package/lib/plugin-runtime/index.d.ts +2 -0
  74. package/lib/plugin-runtime/index.js +2 -0
  75. package/lib/plugin-runtime/index.js.map +1 -1
  76. package/lib/prompt/assembly.js +0 -9
  77. package/lib/prompt/assembly.js.map +1 -1
  78. package/lib/prompt/git-context.js +1 -1
  79. package/lib/prompt/git-context.js.map +1 -1
  80. package/lib/prompt/rules/chat-rules.js +8 -9
  81. package/lib/prompt/rules/chat-rules.js.map +1 -1
  82. package/lib/prompt/rules/critical-rules.js +10 -10
  83. package/lib/prompt/rules/critical-rules.js.map +1 -1
  84. package/lib/prompt/system-prompt.js +4 -5
  85. package/lib/prompt/system-prompt.js.map +1 -1
  86. package/lib/security/file-role-policy.js +6 -6
  87. package/lib/security/file-role-policy.js.map +1 -1
  88. package/lib/security/policy-facade.js +1 -1
  89. package/lib/security/policy-facade.js.map +1 -1
  90. package/lib/security/usage-quota.d.ts +4 -0
  91. package/lib/security/usage-quota.js +23 -2
  92. package/lib/security/usage-quota.js.map +1 -1
  93. package/lib/session/approval-port.d.ts +2 -0
  94. package/lib/session/approval-port.js.map +1 -1
  95. package/lib/session/approval-review-agent.d.ts +1 -1
  96. package/lib/session/approval-review-agent.js +19 -4
  97. package/lib/session/approval-review-agent.js.map +1 -1
  98. package/lib/session/turn-ingress-session.js +7 -1
  99. package/lib/session/turn-ingress-session.js.map +1 -1
  100. package/lib/tool/tool-approval-gate.js +4 -2
  101. package/lib/tool/tool-approval-gate.js.map +1 -1
  102. package/lib/tool/turn-tool-runtime.js +24 -5
  103. package/lib/tool/turn-tool-runtime.js.map +1 -1
  104. package/lib/tool-catalog/deferred-turn-controller.js +1 -4
  105. package/lib/tool-catalog/deferred-turn-controller.js.map +1 -1
  106. package/lib/trace/context-budget.d.ts +4 -3
  107. package/lib/trace/context-budget.js +28 -8
  108. package/lib/trace/context-budget.js.map +1 -1
  109. package/lib/trace/turn-trace-format.js +4 -1
  110. package/lib/trace/turn-trace-format.js.map +1 -1
  111. package/lib/trace/turn-trace.d.ts +6 -0
  112. package/lib/trace/turn-trace.js +8 -2
  113. package/lib/trace/turn-trace.js.map +1 -1
  114. package/lib/trace/turn-usage-log.d.ts +14 -0
  115. package/lib/trace/turn-usage-log.js +83 -0
  116. package/lib/trace/turn-usage-log.js.map +1 -0
  117. package/lib/usage/usage-store.d.ts +199 -0
  118. package/lib/usage/usage-store.js +121 -0
  119. package/lib/usage/usage-store.js.map +1 -0
  120. package/package.json +12 -12
@@ -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;
@@ -1,6 +1,3 @@
1
- /**
2
- * Schedule JobStore — schedule-jobs.json 持久化
3
- */
4
1
  import * as fs from 'node:fs';
5
2
  import * as path from 'node:path';
6
3
  import { getLogger } from '@zhin.js/logger';
@@ -17,6 +14,30 @@ export class ScheduleJobStore {
17
14
  filePath;
18
15
  defaultNotify;
19
16
  cache = null;
17
+ database;
18
+ /** Must run before loading the existing scheduler or publishing this generation. */
19
+ async activateDatabase(database) {
20
+ if (this.database)
21
+ throw new Error('Schedule storage already activated');
22
+ await database.activate(async () => {
23
+ let raw;
24
+ try {
25
+ raw = await fs.promises.readFile(this.filePath, 'utf-8');
26
+ }
27
+ catch (error) {
28
+ if (error.code === 'ENOENT')
29
+ return this.emptyStore();
30
+ throw error;
31
+ }
32
+ const value = JSON.parse(raw);
33
+ if (!value || !Array.isArray(value.jobs))
34
+ throw new Error('Invalid schedule-jobs file; migration did not change the original');
35
+ return { version: value.version ?? SCHEDULE_JOBS_VERSION,
36
+ jobs: value.jobs.map(job => normalizeJob(job, this.defaultNotify)) };
37
+ });
38
+ this.cache = null;
39
+ this.database = database;
40
+ }
20
41
  constructor(options) {
21
42
  this.dataDir = options.dataDir;
22
43
  this.filePath = getScheduleJobsPath(options.dataDir);
@@ -29,6 +50,8 @@ export class ScheduleJobStore {
29
50
  return { version: SCHEDULE_JOBS_VERSION, jobs: [] };
30
51
  }
31
52
  async read() {
53
+ if (this.database)
54
+ return this.database.read();
32
55
  if (this.cache)
33
56
  return this.cache;
34
57
  try {
@@ -57,6 +80,8 @@ export class ScheduleJobStore {
57
80
  this.cache = null;
58
81
  }
59
82
  async write(store) {
83
+ if (this.database)
84
+ return this.database.update(file => { file.jobs = store.jobs; file.version = store.version; });
60
85
  await fs.promises.mkdir(path.dirname(this.filePath), { recursive: true });
61
86
  await fs.promises.writeFile(this.filePath, JSON.stringify(store, null, 2), 'utf-8');
62
87
  this.cache = store;
@@ -66,10 +91,17 @@ export class ScheduleJobStore {
66
91
  return store.jobs;
67
92
  }
68
93
  async getJob(id) {
94
+ if (this.database)
95
+ return this.database.get(id);
69
96
  const store = await this.read();
70
97
  return store.jobs.find((j) => j.id === id);
71
98
  }
72
99
  async upsertJob(job) {
100
+ if (this.database)
101
+ return this.database.mutateJob(job.id, existing => ({ result: undefined,
102
+ job: existing && isSameScheduleJobBusiness(existing, job) ? existing : { ...job,
103
+ createdAt: existing?.createdAt ?? (job.createdAt || Date.now()), updatedAt: Date.now() },
104
+ }));
73
105
  const store = await this.read();
74
106
  const idx = store.jobs.findIndex((j) => j.id === job.id);
75
107
  const now = Date.now();
@@ -86,6 +118,8 @@ export class ScheduleJobStore {
86
118
  await this.write(store);
87
119
  }
88
120
  async removeJob(id) {
121
+ if (this.database)
122
+ return this.database.mutateJob(id, job => ({ job: undefined, result: job !== undefined }));
89
123
  const store = await this.read();
90
124
  const before = store.jobs.length;
91
125
  store.jobs = store.jobs.filter((j) => j.id !== id);
@@ -96,6 +130,10 @@ export class ScheduleJobStore {
96
130
  return false;
97
131
  }
98
132
  async updateJobState(id, patch) {
133
+ if (this.database)
134
+ return this.database.mutateJob(id, job => ({ result: undefined,
135
+ job: job ? { ...job, updatedAt: Date.now(), state: { ...job.state, ...patch } } : undefined,
136
+ }));
99
137
  const job = await this.getJob(id);
100
138
  if (!job)
101
139
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../src/assistant/job-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Schedule JobStore — schedule-jobs.json 持久化\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { formatCompact, getLogger } from '@zhin.js/logger';\nimport { type ScheduleJob, type ScheduleJobFile, type JobAction, type JobNotify, SCHEDULE_JOBS_FILENAME, SCHEDULE_JOBS_VERSION } from './types.js';\nimport { parseScheduleJobCreator } from './job-creator.js';\nimport { parseScheduleJobExecutionPlan } from './schedule-execution.js';\nimport { parseJobNotify, resolveEffectiveNotify } from './notification-router.js';\nconst logger = getLogger('schedule-job-store');\n\nexport interface ScheduleJobStoreOptions {\n dataDir: string;\n /** 读盘时合并 `{ channel: im }` 等缺 target 的 notify(与 resolveEffectiveNotify 一致) */\n defaultNotify?: JobNotify;\n}\n\nexport function getScheduleJobsPath(dataDir: string): string {\n return path.join(dataDir, SCHEDULE_JOBS_FILENAME);\n}\n\nexport class ScheduleJobStore {\n private dataDir: string;\n private filePath: string;\n private defaultNotify?: JobNotify;\n private cache: ScheduleJobFile | null = null;\n\n constructor(options: ScheduleJobStoreOptions) {\n this.dataDir = options.dataDir;\n this.filePath = getScheduleJobsPath(options.dataDir);\n this.defaultNotify = options.defaultNotify;\n }\n\n getDataDir(): string {\n return this.dataDir;\n }\n\n private emptyStore(): ScheduleJobFile {\n return { version: SCHEDULE_JOBS_VERSION, jobs: [] };\n }\n\n async read(): Promise<ScheduleJobFile> {\n if (this.cache) return this.cache;\n try {\n const raw = await fs.promises.readFile(this.filePath, 'utf-8');\n const data = JSON.parse(raw) as ScheduleJobFile;\n if (!data || !Array.isArray(data.jobs)) {\n this.cache = this.emptyStore();\n return this.cache;\n }\n this.cache = {\n version: data.version ?? SCHEDULE_JOBS_VERSION,\n jobs: (data.jobs as unknown[]).map((j) =>\n normalizeJob(j as Record<string, unknown>, this.defaultNotify),\n ),\n };\n return this.cache;\n } catch (e: unknown) {\n const err = e as NodeJS.ErrnoException;\n if (err?.code === 'ENOENT') {\n this.cache = this.emptyStore();\n return this.cache;\n }\n throw e;\n }\n }\n\n private invalidateCache(): void {\n this.cache = null;\n }\n\n async write(store: ScheduleJobFile): Promise<void> {\n await fs.promises.mkdir(path.dirname(this.filePath), { recursive: true });\n await fs.promises.writeFile(this.filePath, JSON.stringify(store, null, 2), 'utf-8');\n this.cache = store;\n }\n\n async listJobs(): Promise<ScheduleJob[]> {\n const store = await this.read();\n return store.jobs;\n }\n\n async getJob(id: string): Promise<ScheduleJob | undefined> {\n const store = await this.read();\n return store.jobs.find((j) => j.id === id);\n }\n\n async upsertJob(job: ScheduleJob): Promise<void> {\n const store = await this.read();\n const idx = store.jobs.findIndex((j) => j.id === job.id);\n const now = Date.now();\n if (idx >= 0) {\n const existing = store.jobs[idx]!;\n if (isSameScheduleJobBusiness(existing, job)) {\n return;\n }\n store.jobs[idx] = { ...job, createdAt: existing.createdAt, updatedAt: now };\n } else {\n store.jobs.push({ ...job, updatedAt: now, createdAt: job.createdAt || now });\n }\n await this.write(store);\n }\n\n async removeJob(id: string): Promise<boolean> {\n const store = await this.read();\n const before = store.jobs.length;\n store.jobs = store.jobs.filter((j) => j.id !== id);\n if (store.jobs.length < before) {\n await this.write(store);\n return true;\n }\n return false;\n }\n\n async updateJobState(id: string, patch: Partial<ScheduleJob['state']>): Promise<void> {\n const job = await this.getJob(id);\n if (!job) return;\n job.state = { ...job.state, ...patch };\n job.updatedAt = Date.now();\n await this.upsertJob(job);\n }\n\n async findEventJobByEventId(eventId: string): Promise<ScheduleJob | undefined> {\n const jobs = await this.listJobs();\n return jobs.find((j) => j.schedule.kind === 'event' && j.schedule.eventId === eventId);\n }\n\n async createEventJob(input: {\n id: string;\n label?: string;\n action: JobAction;\n notify?: JobNotify;\n source: string;\n eventType?: string;\n eventId?: string;\n payload?: unknown;\n }): Promise<ScheduleJob> {\n const now = Date.now();\n const job: ScheduleJob = {\n id: input.id,\n label: input.label,\n enabled: true,\n schedule: {\n kind: 'event',\n eventId: input.eventId,\n source: input.source,\n eventType: input.eventType,\n },\n action: input.action,\n notify: input.notify ?? { channel: 'silent' },\n createdAt: now,\n updatedAt: now,\n state: {},\n source: 'event',\n eventPayload: input.payload,\n };\n await this.upsertJob(job);\n return job;\n }\n}\n\n/** Compare business fields; ignore timestamps that upsert always refreshes. */\nfunction isSameScheduleJobBusiness(a: ScheduleJob, b: ScheduleJob): boolean {\n return JSON.stringify(scheduleJobBusiness(a)) === JSON.stringify(scheduleJobBusiness(b));\n}\n\nfunction scheduleJobBusiness(job: ScheduleJob): unknown {\n return {\n id: job.id,\n label: job.label,\n enabled: job.enabled,\n schedule: job.schedule,\n action: job.action,\n notify: job.notify,\n notifyOnFailure: job.notifyOnFailure,\n state: job.state ?? {},\n source: job.source,\n createdBy: job.createdBy,\n executionPlan: job.executionPlan,\n activityFeedback: job.activityFeedback,\n budget: job.budget,\n eventPayload: job.eventPayload,\n };\n}\n\nfunction normalizeJob(raw: Record<string, unknown>, defaultNotify?: JobNotify): ScheduleJob {\n const notify = parseJobNotify(\n resolveEffectiveNotify(raw.notify as JobNotify | undefined, defaultNotify),\n );\n const schedule = raw.schedule as ScheduleJob['schedule'];\n const action = raw.action as JobAction;\n if (!schedule || !action) {\n throw new Error(`schedule-jobs.json: job \"${String(raw.id)}\" 缺少 schedule 或 action`);\n }\n return {\n id: String(raw.id),\n label: raw.label != null ? String(raw.label) : undefined,\n enabled: raw.enabled !== false,\n schedule,\n action,\n notify,\n notifyOnFailure: raw.notifyOnFailure === true ? true : undefined,\n createdAt: Number(raw.createdAt) || Date.now(),\n updatedAt: Number(raw.updatedAt) || Date.now(),\n state: (raw.state as ScheduleJob['state']) ?? {},\n source: raw.source as ScheduleJob['source'],\n createdBy: parseScheduleJobCreator(raw.createdBy),\n executionPlan: parseScheduleJobExecutionPlan(raw.executionPlan),\n activityFeedback: raw.activityFeedback === true ? true : undefined,\n budget: raw.budget && typeof raw.budget === 'object'\n ? raw.budget as ScheduleJob['budget']\n : undefined,\n eventPayload: raw.eventPayload,\n };\n}\n\nexport function createScheduleJobStoreFromConfig(\n dataDir: string,\n options?: { defaultNotify?: JobNotify },\n): ScheduleJobStore {\n return new ScheduleJobStore({ dataDir, defaultNotify: options?.defaultNotify });\n}\n"]}
1
+ {"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../src/assistant/job-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Schedule JobStore — SQL persistence with the existing file mode as fallback\n */\nimport type { DatabaseScheduleJobStorage } from './job-storage.js';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { formatCompact, getLogger } from '@zhin.js/logger';\nimport { type ScheduleJob, type ScheduleJobFile, type JobAction, type JobNotify, SCHEDULE_JOBS_FILENAME, SCHEDULE_JOBS_VERSION } from './types.js';\nimport { parseScheduleJobCreator } from './job-creator.js';\nimport { parseScheduleJobExecutionPlan } from './schedule-execution.js';\nimport { parseJobNotify, resolveEffectiveNotify } from './notification-router.js';\nconst logger = getLogger('schedule-job-store');\n\nexport interface ScheduleJobStoreOptions {\n dataDir: string;\n /** 读盘时合并 `{ channel: im }` 等缺 target 的 notify(与 resolveEffectiveNotify 一致) */\n defaultNotify?: JobNotify;\n}\n\nexport function getScheduleJobsPath(dataDir: string): string {\n return path.join(dataDir, SCHEDULE_JOBS_FILENAME);\n}\n\nexport class ScheduleJobStore {\n private dataDir: string;\n private filePath: string;\n private defaultNotify?: JobNotify;\n private cache: ScheduleJobFile | null = null;\n private database?: DatabaseScheduleJobStorage;\n\n /** Must run before loading the existing scheduler or publishing this generation. */\n async activateDatabase(database: DatabaseScheduleJobStorage): Promise<void> {\n if (this.database) throw new Error('Schedule storage already activated');\n await database.activate(async () => {\n let raw: string;\n try { raw = await fs.promises.readFile(this.filePath, 'utf-8'); }\n catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return this.emptyStore(); throw error; }\n const value = JSON.parse(raw) as ScheduleJobFile;\n if (!value || !Array.isArray(value.jobs)) throw new Error('Invalid schedule-jobs file; migration did not change the original');\n return { version: value.version ?? SCHEDULE_JOBS_VERSION,\n jobs: value.jobs.map(job => normalizeJob(job as unknown as Record<string, unknown>, this.defaultNotify)) };\n });\n this.cache = null;\n this.database = database;\n }\n\n constructor(options: ScheduleJobStoreOptions) {\n this.dataDir = options.dataDir;\n this.filePath = getScheduleJobsPath(options.dataDir);\n this.defaultNotify = options.defaultNotify;\n }\n\n getDataDir(): string {\n return this.dataDir;\n }\n\n private emptyStore(): ScheduleJobFile {\n return { version: SCHEDULE_JOBS_VERSION, jobs: [] };\n }\n\n async read(): Promise<ScheduleJobFile> {\n if (this.database) return this.database.read();\n if (this.cache) return this.cache;\n try {\n const raw = await fs.promises.readFile(this.filePath, 'utf-8');\n const data = JSON.parse(raw) as ScheduleJobFile;\n if (!data || !Array.isArray(data.jobs)) {\n this.cache = this.emptyStore();\n return this.cache;\n }\n this.cache = {\n version: data.version ?? SCHEDULE_JOBS_VERSION,\n jobs: (data.jobs as unknown[]).map((j) =>\n normalizeJob(j as Record<string, unknown>, this.defaultNotify),\n ),\n };\n return this.cache;\n } catch (e: unknown) {\n const err = e as NodeJS.ErrnoException;\n if (err?.code === 'ENOENT') {\n this.cache = this.emptyStore();\n return this.cache;\n }\n throw e;\n }\n }\n\n private invalidateCache(): void {\n this.cache = null;\n }\n\n async write(store: ScheduleJobFile): Promise<void> {\n if (this.database) return this.database.update(file => { file.jobs = store.jobs; file.version = store.version; });\n await fs.promises.mkdir(path.dirname(this.filePath), { recursive: true });\n await fs.promises.writeFile(this.filePath, JSON.stringify(store, null, 2), 'utf-8');\n this.cache = store;\n }\n\n async listJobs(): Promise<ScheduleJob[]> {\n const store = await this.read();\n return store.jobs;\n }\n\n async getJob(id: string): Promise<ScheduleJob | undefined> {\n if (this.database) return this.database.get(id);\n const store = await this.read();\n return store.jobs.find((j) => j.id === id);\n }\n\n async upsertJob(job: ScheduleJob): Promise<void> {\n if (this.database) return this.database.mutateJob(job.id, existing => ({ result: undefined,\n job: existing && isSameScheduleJobBusiness(existing, job) ? existing : { ...job,\n createdAt: existing?.createdAt ?? (job.createdAt || Date.now()), updatedAt: Date.now() },\n }));\n const store = await this.read();\n const idx = store.jobs.findIndex((j) => j.id === job.id);\n const now = Date.now();\n if (idx >= 0) {\n const existing = store.jobs[idx]!;\n if (isSameScheduleJobBusiness(existing, job)) {\n return;\n }\n store.jobs[idx] = { ...job, createdAt: existing.createdAt, updatedAt: now };\n } else {\n store.jobs.push({ ...job, updatedAt: now, createdAt: job.createdAt || now });\n }\n await this.write(store);\n }\n\n async removeJob(id: string): Promise<boolean> {\n if (this.database) return this.database.mutateJob(id, job => ({ job: undefined, result: job !== undefined }));\n const store = await this.read();\n const before = store.jobs.length;\n store.jobs = store.jobs.filter((j) => j.id !== id);\n if (store.jobs.length < before) {\n await this.write(store);\n return true;\n }\n return false;\n }\n\n async updateJobState(id: string, patch: Partial<ScheduleJob['state']>): Promise<void> {\n if (this.database) return this.database.mutateJob(id, job => ({ result: undefined,\n job: job ? { ...job, updatedAt: Date.now(), state: { ...job.state, ...patch } } : undefined,\n }));\n const job = await this.getJob(id);\n if (!job) return;\n job.state = { ...job.state, ...patch };\n job.updatedAt = Date.now();\n await this.upsertJob(job);\n }\n\n async findEventJobByEventId(eventId: string): Promise<ScheduleJob | undefined> {\n const jobs = await this.listJobs();\n return jobs.find((j) => j.schedule.kind === 'event' && j.schedule.eventId === eventId);\n }\n\n async createEventJob(input: {\n id: string;\n label?: string;\n action: JobAction;\n notify?: JobNotify;\n source: string;\n eventType?: string;\n eventId?: string;\n payload?: unknown;\n }): Promise<ScheduleJob> {\n const now = Date.now();\n const job: ScheduleJob = {\n id: input.id,\n label: input.label,\n enabled: true,\n schedule: {\n kind: 'event',\n eventId: input.eventId,\n source: input.source,\n eventType: input.eventType,\n },\n action: input.action,\n notify: input.notify ?? { channel: 'silent' },\n createdAt: now,\n updatedAt: now,\n state: {},\n source: 'event',\n eventPayload: input.payload,\n };\n await this.upsertJob(job);\n return job;\n }\n}\n\n/** Compare business fields; ignore timestamps that upsert always refreshes. */\nfunction isSameScheduleJobBusiness(a: ScheduleJob, b: ScheduleJob): boolean {\n return JSON.stringify(scheduleJobBusiness(a)) === JSON.stringify(scheduleJobBusiness(b));\n}\n\nfunction scheduleJobBusiness(job: ScheduleJob): unknown {\n return {\n id: job.id,\n label: job.label,\n enabled: job.enabled,\n schedule: job.schedule,\n action: job.action,\n notify: job.notify,\n notifyOnFailure: job.notifyOnFailure,\n state: job.state ?? {},\n source: job.source,\n createdBy: job.createdBy,\n executionPlan: job.executionPlan,\n activityFeedback: job.activityFeedback,\n budget: job.budget,\n eventPayload: job.eventPayload,\n };\n}\n\nfunction normalizeJob(raw: Record<string, unknown>, defaultNotify?: JobNotify): ScheduleJob {\n const notify = parseJobNotify(\n resolveEffectiveNotify(raw.notify as JobNotify | undefined, defaultNotify),\n );\n const schedule = raw.schedule as ScheduleJob['schedule'];\n const action = raw.action as JobAction;\n if (!schedule || !action) {\n throw new Error(`schedule-jobs.json: job \"${String(raw.id)}\" 缺少 schedule 或 action`);\n }\n return {\n id: String(raw.id),\n label: raw.label != null ? String(raw.label) : undefined,\n enabled: raw.enabled !== false,\n schedule,\n action,\n notify,\n notifyOnFailure: raw.notifyOnFailure === true ? true : undefined,\n createdAt: Number(raw.createdAt) || Date.now(),\n updatedAt: Number(raw.updatedAt) || Date.now(),\n state: (raw.state as ScheduleJob['state']) ?? {},\n source: raw.source as ScheduleJob['source'],\n createdBy: parseScheduleJobCreator(raw.createdBy),\n executionPlan: parseScheduleJobExecutionPlan(raw.executionPlan),\n activityFeedback: raw.activityFeedback === true ? true : undefined,\n budget: raw.budget && typeof raw.budget === 'object'\n ? raw.budget as ScheduleJob['budget']\n : undefined,\n eventPayload: raw.eventPayload,\n };\n}\n\nexport function createScheduleJobStoreFromConfig(\n dataDir: string,\n options?: { defaultNotify?: JobNotify },\n): ScheduleJobStore {\n return new ScheduleJobStore({ dataDir, defaultNotify: options?.defaultNotify });\n}\n"]}
@@ -9,6 +9,11 @@ export declare const PERCEPTION_MEDIA_KINDS: readonly PerceptionMediaKind[];
9
9
  export interface PerceptionConfigInput {
10
10
  provider?: string;
11
11
  model?: string;
12
+ /** 按顺序尝试的备用识图模型,不改变主对话模型。 */
13
+ fallbacks?: readonly {
14
+ provider: string;
15
+ model: string;
16
+ }[];
12
17
  kinds?: readonly string[];
13
18
  maxChars?: number;
14
19
  timeoutMs?: number;
@@ -17,6 +22,10 @@ export interface PerceptionConfigInput {
17
22
  export interface ResolvedPerceptionConfig {
18
23
  readonly provider: string;
19
24
  readonly model: string;
25
+ readonly fallbacks?: readonly {
26
+ readonly provider: string;
27
+ readonly model: string;
28
+ }[];
20
29
  readonly kinds: readonly PerceptionMediaKind[];
21
30
  readonly maxChars: number;
22
31
  readonly timeoutMs: number;