@deepseek-ai/dsh-schedule 0.2.0-rc.2 → 0.2.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +14 -14
- package/README.md +6 -5
- package/README.zh.md +6 -5
- package/lib/index.js +37 -645
- package/lib/typert.host.js +16 -9
- package/lib/typert.remote-client.js +8 -5
- package/lib/types/domain.d.ts +3 -3
- package/lib/types/domain.js +1 -1
- package/lib/types/index.d.ts +24 -5
- package/lib/types/index.js +40 -30
- package/lib/types/types.d.ts +6 -1
- package/package.json +35 -40
- package/lib/invariant.js +0 -370
- package/lib/types/invariant.d.ts +0 -16
- package/lib/types/invariant.js +0 -58
- package/lib/types/tools.d.ts +0 -15
- package/lib/types/tools.js +0 -505
package/lib/types/tools.js
DELETED
|
@@ -1,505 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent-scoped consumers of the shared Host Schedule management service.
|
|
3
|
-
* @module @deepseek-ai/dsh-schedule
|
|
4
|
-
*/
|
|
5
|
-
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
6
|
-
import { MAX_TITLE_LENGTH, MIN_EVERY_INTERVAL_SECONDS, REQUIRED_TITLE_MESSAGE, ScheduleId, ScheduleInputError, scheduleView } from "./domain.js";
|
|
7
|
-
const SHARED_VIEW_PROPERTIES = {
|
|
8
|
-
id: { type: 'string', required: true },
|
|
9
|
-
title: { type: 'string', required: true },
|
|
10
|
-
prompt: { type: 'string', required: true },
|
|
11
|
-
scheduledAt: { type: 'string', required: true },
|
|
12
|
-
state: { type: 'string', required: true, enum: ['scheduled', 'overdue'] },
|
|
13
|
-
deliveryMode: { type: 'string', required: true, const: 'host' },
|
|
14
|
-
};
|
|
15
|
-
const AFTER_VIEW_SCHEMA = {
|
|
16
|
-
type: 'object',
|
|
17
|
-
additionalProperties: false,
|
|
18
|
-
properties: {
|
|
19
|
-
...SHARED_VIEW_PROPERTIES,
|
|
20
|
-
kind: { type: 'string', required: true, const: 'after' },
|
|
21
|
-
afterSeconds: { type: 'integer', required: true },
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
const AT_VIEW_SCHEMA = {
|
|
25
|
-
type: 'object',
|
|
26
|
-
additionalProperties: false,
|
|
27
|
-
properties: {
|
|
28
|
-
...SHARED_VIEW_PROPERTIES,
|
|
29
|
-
kind: { type: 'string', required: true, const: 'at' },
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
const EVERY_VIEW_SCHEMA = {
|
|
33
|
-
type: 'object',
|
|
34
|
-
additionalProperties: false,
|
|
35
|
-
properties: {
|
|
36
|
-
...SHARED_VIEW_PROPERTIES,
|
|
37
|
-
kind: { type: 'string', required: true, const: 'every' },
|
|
38
|
-
everySeconds: { type: 'integer', required: true },
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
const DAILY_VIEW_SCHEMA = {
|
|
42
|
-
type: 'object',
|
|
43
|
-
additionalProperties: false,
|
|
44
|
-
properties: {
|
|
45
|
-
...SHARED_VIEW_PROPERTIES,
|
|
46
|
-
kind: { type: 'string', required: true, const: 'daily' },
|
|
47
|
-
time: { type: 'string', required: true },
|
|
48
|
-
timeZone: { type: 'string', required: true },
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
const WEEKLY_VIEW_SCHEMA = {
|
|
52
|
-
type: 'object',
|
|
53
|
-
additionalProperties: false,
|
|
54
|
-
properties: {
|
|
55
|
-
...SHARED_VIEW_PROPERTIES,
|
|
56
|
-
kind: { type: 'string', required: true, const: 'weekly' },
|
|
57
|
-
time: { type: 'string', required: true },
|
|
58
|
-
timeZone: { type: 'string', required: true },
|
|
59
|
-
weekdays: { type: 'array', required: true, items: { type: 'integer' } },
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
const CRON_VIEW_SCHEMA = {
|
|
63
|
-
type: 'object',
|
|
64
|
-
additionalProperties: false,
|
|
65
|
-
properties: {
|
|
66
|
-
...SHARED_VIEW_PROPERTIES,
|
|
67
|
-
kind: { type: 'string', required: true, const: 'cron' },
|
|
68
|
-
expression: { type: 'string', required: true },
|
|
69
|
-
timeZone: { type: 'string', required: true },
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
const VIEW_SCHEMA = {
|
|
73
|
-
oneOf: [
|
|
74
|
-
AFTER_VIEW_SCHEMA, AT_VIEW_SCHEMA, EVERY_VIEW_SCHEMA, DAILY_VIEW_SCHEMA, WEEKLY_VIEW_SCHEMA, CRON_VIEW_SCHEMA,
|
|
75
|
-
],
|
|
76
|
-
};
|
|
77
|
-
/** Build one exact two-field error schema while preserving its literal code. */
|
|
78
|
-
function basicErrorSchema(code) {
|
|
79
|
-
return {
|
|
80
|
-
type: 'object',
|
|
81
|
-
additionalProperties: false,
|
|
82
|
-
properties: {
|
|
83
|
-
code: { type: 'string', required: true, const: code },
|
|
84
|
-
message: { type: 'string', required: true },
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
const ERROR_SCHEMAS = [
|
|
89
|
-
basicErrorSchema('invalid_prompt'),
|
|
90
|
-
basicErrorSchema('invalid_selector'),
|
|
91
|
-
basicErrorSchema('invalid_rule'),
|
|
92
|
-
basicErrorSchema('invalid_time_zone'),
|
|
93
|
-
basicErrorSchema('not_future'),
|
|
94
|
-
basicErrorSchema('time_out_of_range'),
|
|
95
|
-
basicErrorSchema('frequency_too_high'),
|
|
96
|
-
basicErrorSchema('internal_error'),
|
|
97
|
-
];
|
|
98
|
-
const CREATE_OUTPUT_SCHEMA = { oneOf: [VIEW_SCHEMA, ...ERROR_SCHEMAS] };
|
|
99
|
-
const LIST_OUTPUT_SCHEMA = {
|
|
100
|
-
oneOf: [
|
|
101
|
-
{ type: 'array', items: VIEW_SCHEMA },
|
|
102
|
-
...ERROR_SCHEMAS,
|
|
103
|
-
],
|
|
104
|
-
};
|
|
105
|
-
const DELETE_OUTPUT_SCHEMA = {
|
|
106
|
-
oneOf: [
|
|
107
|
-
{
|
|
108
|
-
type: 'object',
|
|
109
|
-
additionalProperties: false,
|
|
110
|
-
properties: {
|
|
111
|
-
id: { type: 'string', required: true },
|
|
112
|
-
deleted: { type: 'boolean', required: true, const: true },
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
type: 'object',
|
|
117
|
-
additionalProperties: false,
|
|
118
|
-
properties: {
|
|
119
|
-
id: { type: 'string', required: true },
|
|
120
|
-
deleted: { type: 'boolean', required: true, const: false },
|
|
121
|
-
code: { type: 'string', required: true, const: 'schedule_not_found' },
|
|
122
|
-
},
|
|
123
|
-
},
|
|
124
|
-
...ERROR_SCHEMAS,
|
|
125
|
-
],
|
|
126
|
-
};
|
|
127
|
-
const UPDATE_OUTPUT_SCHEMA = {
|
|
128
|
-
oneOf: [
|
|
129
|
-
VIEW_SCHEMA,
|
|
130
|
-
{
|
|
131
|
-
type: 'object',
|
|
132
|
-
additionalProperties: false,
|
|
133
|
-
properties: {
|
|
134
|
-
id: { type: 'string', required: true },
|
|
135
|
-
updated: { type: 'boolean', required: true, const: false },
|
|
136
|
-
code: {
|
|
137
|
-
type: 'string',
|
|
138
|
-
required: true,
|
|
139
|
-
enum: ['schedule_not_found', 'schedule_ended', 'schedule_conflict'],
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
...ERROR_SCHEMAS,
|
|
144
|
-
],
|
|
145
|
-
};
|
|
146
|
-
const CREATE_DESCRIPTION = 'Create a reminder in the current session that delivers prompt when it becomes due. '
|
|
147
|
-
+ 'Supply exactly one timing parameter: after_seconds, at, every_seconds, daily, weekly, or cron. '
|
|
148
|
-
+ 'Local times that do not exist in the zone are skipped; repeated local times fire once, at the earlier instant. '
|
|
149
|
-
+ 'After downtime, a recurring reminder delivers only its latest missed occurrence. Delivery can repeat after a crash.';
|
|
150
|
-
const LIST_DESCRIPTION = 'List the active reminders in the current session.';
|
|
151
|
-
const DELETE_DESCRIPTION = 'Delete a reminder in the current session, active or inactive. Deletion does not retract a reminder message that is already queued.';
|
|
152
|
-
const UPDATE_DESCRIPTION = 'Change a reminder in place, keeping its id. Supply a new title, prompt, or at most one timing parameter; '
|
|
153
|
-
+ 'omitted fields keep their stored values. To change a relative delay, create a new reminder.';
|
|
154
|
-
/** Deterministic model content for every canonical Schedule value. */
|
|
155
|
-
function renderValue(_args, value) {
|
|
156
|
-
// The ToolRuntime has already validated the value against the lossless-JSON output schema.
|
|
157
|
-
const text = JSON.stringify(value);
|
|
158
|
-
return [{ type: 'text', text }];
|
|
159
|
-
}
|
|
160
|
-
/** Pure generic pending card. */
|
|
161
|
-
function present(title, kind, rawInput) {
|
|
162
|
-
return { card: 'generic', title, kind, ...rawInput === undefined ? {} : { rawInput } };
|
|
163
|
-
}
|
|
164
|
-
/** Stable error for failures not safe to expose. */
|
|
165
|
-
function internalError() {
|
|
166
|
-
return { code: 'internal_error', message: 'The schedule operation failed.' };
|
|
167
|
-
}
|
|
168
|
-
/** Translate invalid input while withholding internal storage failures. */
|
|
169
|
-
function operationError(error) {
|
|
170
|
-
return error instanceof ScheduleInputError ? { code: error.code, message: error.message } : internalError();
|
|
171
|
-
}
|
|
172
|
-
/** One supplied fixed-rate interval: a safe integer at or above the Host floor, or undefined. */
|
|
173
|
-
function invalidInterval(everySeconds) {
|
|
174
|
-
if (everySeconds === undefined)
|
|
175
|
-
return undefined;
|
|
176
|
-
if (!Number.isSafeInteger(everySeconds)) {
|
|
177
|
-
return { code: 'invalid_rule', message: 'every_seconds must be a safe integer.' };
|
|
178
|
-
}
|
|
179
|
-
if (everySeconds < MIN_EVERY_INTERVAL_SECONDS) {
|
|
180
|
-
return {
|
|
181
|
-
code: 'frequency_too_high',
|
|
182
|
-
message: `every_seconds must be at least ${MIN_EVERY_INTERVAL_SECONDS}.`,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
return undefined;
|
|
186
|
-
}
|
|
187
|
-
/** Validate selector constraints that the open parameter root cannot express. */
|
|
188
|
-
function validateCreateArgs(args) {
|
|
189
|
-
const keys = Object.keys(args);
|
|
190
|
-
if (keys.some(key => key !== 'prompt'
|
|
191
|
-
&& key !== 'title'
|
|
192
|
-
&& key !== 'after_seconds'
|
|
193
|
-
&& key !== 'at'
|
|
194
|
-
&& key !== 'every_seconds'
|
|
195
|
-
&& key !== 'daily'
|
|
196
|
-
&& key !== 'weekly'
|
|
197
|
-
&& key !== 'cron')
|
|
198
|
-
|| Number(args.after_seconds !== undefined)
|
|
199
|
-
+ Number(args.at !== undefined)
|
|
200
|
-
+ Number(args.every_seconds !== undefined)
|
|
201
|
-
+ Number(args.daily !== undefined)
|
|
202
|
-
+ Number(args.weekly !== undefined)
|
|
203
|
-
+ Number(args.cron !== undefined) !== 1) {
|
|
204
|
-
return {
|
|
205
|
-
code: 'invalid_selector',
|
|
206
|
-
message: 'schedule_create accepts exactly one of after_seconds, at, every_seconds, daily, weekly, or cron.',
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
if (args.prompt.trim().length === 0) {
|
|
210
|
-
return { code: 'invalid_prompt', message: 'prompt must be non-empty after trimming.' };
|
|
211
|
-
}
|
|
212
|
-
if (args.title.trim().length === 0) {
|
|
213
|
-
return { code: 'invalid_prompt', message: REQUIRED_TITLE_MESSAGE };
|
|
214
|
-
}
|
|
215
|
-
if (args.title.trim().length > MAX_TITLE_LENGTH) {
|
|
216
|
-
return { code: 'invalid_prompt', message: `title must be at most ${MAX_TITLE_LENGTH} characters.` };
|
|
217
|
-
}
|
|
218
|
-
if (args.after_seconds !== undefined
|
|
219
|
-
&& (!Number.isSafeInteger(args.after_seconds) || args.after_seconds <= 0)) {
|
|
220
|
-
return { code: 'invalid_rule', message: 'after_seconds must be a positive safe integer.' };
|
|
221
|
-
}
|
|
222
|
-
return invalidInterval(args.every_seconds);
|
|
223
|
-
}
|
|
224
|
-
/** Validate the in-place update's selector count, id, and any supplied name, instruction, or interval. */
|
|
225
|
-
function validateUpdateArgs(args) {
|
|
226
|
-
const selectors = [
|
|
227
|
-
args.at !== undefined,
|
|
228
|
-
args.every_seconds !== undefined,
|
|
229
|
-
args.daily !== undefined,
|
|
230
|
-
args.weekly !== undefined,
|
|
231
|
-
args.cron !== undefined,
|
|
232
|
-
].filter(Boolean).length;
|
|
233
|
-
if (Object.keys(args).some(key => key !== 'id'
|
|
234
|
-
&& key !== 'title'
|
|
235
|
-
&& key !== 'prompt'
|
|
236
|
-
&& key !== 'at'
|
|
237
|
-
&& key !== 'every_seconds'
|
|
238
|
-
&& key !== 'daily'
|
|
239
|
-
&& key !== 'weekly'
|
|
240
|
-
&& key !== 'cron')
|
|
241
|
-
|| selectors > 1) {
|
|
242
|
-
return {
|
|
243
|
-
code: 'invalid_selector',
|
|
244
|
-
message: 'schedule_update accepts at most one of at, every_seconds, daily, weekly, or cron.',
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
if (args.id.length === 0 || args.id.trim() !== args.id) {
|
|
248
|
-
return { code: 'invalid_rule', message: 'schedule_update id must be non-empty without surrounding whitespace.' };
|
|
249
|
-
}
|
|
250
|
-
if (selectors === 0 && args.title === undefined && args.prompt === undefined) {
|
|
251
|
-
return {
|
|
252
|
-
code: 'invalid_selector',
|
|
253
|
-
message: 'schedule_update needs a new title, prompt, or one of at, every_seconds, daily, weekly, or cron.',
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
if (args.title !== undefined && args.title.trim().length === 0) {
|
|
257
|
-
return { code: 'invalid_prompt', message: REQUIRED_TITLE_MESSAGE };
|
|
258
|
-
}
|
|
259
|
-
if (args.title !== undefined && args.title.trim().length > MAX_TITLE_LENGTH) {
|
|
260
|
-
return { code: 'invalid_prompt', message: `title must be at most ${MAX_TITLE_LENGTH} characters.` };
|
|
261
|
-
}
|
|
262
|
-
if (args.prompt !== undefined && args.prompt.trim().length === 0) {
|
|
263
|
-
return { code: 'invalid_prompt', message: 'prompt must be non-empty after trimming.' };
|
|
264
|
-
}
|
|
265
|
-
return invalidInterval(args.every_seconds);
|
|
266
|
-
}
|
|
267
|
-
/** The one timing replacement the update carries, or undefined when the request keeps the committed target. */
|
|
268
|
-
function timingChangeFrom(args) {
|
|
269
|
-
if (args.at !== undefined)
|
|
270
|
-
return { kind: 'at', at: args.at };
|
|
271
|
-
if (args.every_seconds !== undefined)
|
|
272
|
-
return { kind: 'every', every_seconds: args.every_seconds };
|
|
273
|
-
if (args.daily !== undefined)
|
|
274
|
-
return { kind: 'daily', daily: args.daily };
|
|
275
|
-
if (args.weekly !== undefined)
|
|
276
|
-
return { kind: 'weekly', weekly: args.weekly };
|
|
277
|
-
if (args.cron !== undefined)
|
|
278
|
-
return { kind: 'cron', cron: args.cron };
|
|
279
|
-
return undefined;
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Selector parameters shared by `schedule_create` and `schedule_update`, in the order the
|
|
283
|
-
* generated tool catalog states them.
|
|
284
|
-
*/
|
|
285
|
-
const SELECTOR_PARAMETERS = {
|
|
286
|
-
every_seconds: {
|
|
287
|
-
type: 'number',
|
|
288
|
-
description: `Fixed-rate interval in whole seconds, at least ${MIN_EVERY_INTERVAL_SECONDS}, aligned to the creation time; changing it with schedule_update re-aligns it to the save time.`,
|
|
289
|
-
},
|
|
290
|
-
daily: {
|
|
291
|
-
type: 'object',
|
|
292
|
-
additionalProperties: false,
|
|
293
|
-
description: 'Every day at a local time.',
|
|
294
|
-
properties: {
|
|
295
|
-
time: { type: 'string', required: true, description: 'HH:mm:ss with optional 1-3 fractional digits, for example 23:00:00.' },
|
|
296
|
-
time_zone: { type: 'string', required: true, description: 'UTC or IANA Area/Location, for example Asia/Shanghai.' },
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
weekly: {
|
|
300
|
-
type: 'object',
|
|
301
|
-
additionalProperties: false,
|
|
302
|
-
description: 'On the given weekdays at a local time.',
|
|
303
|
-
properties: {
|
|
304
|
-
time: { type: 'string', required: true, description: 'HH:mm:ss with optional 1-3 fractional digits, for example 09:00:00.' },
|
|
305
|
-
time_zone: { type: 'string', required: true, description: 'UTC or IANA Area/Location, for example Asia/Shanghai.' },
|
|
306
|
-
weekdays: {
|
|
307
|
-
type: 'array',
|
|
308
|
-
required: true,
|
|
309
|
-
description: 'ISO weekdays, Monday 1 through Sunday 7, without repetitions.',
|
|
310
|
-
items: { type: 'integer' },
|
|
311
|
-
},
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
cron: {
|
|
315
|
-
type: 'object',
|
|
316
|
-
additionalProperties: false,
|
|
317
|
-
description: 'Five-field Vixie cron expression in a time zone.',
|
|
318
|
-
properties: {
|
|
319
|
-
expression: {
|
|
320
|
-
type: 'string',
|
|
321
|
-
required: true,
|
|
322
|
-
description: 'minute hour day-of-month month day-of-week, for example "*/15 9-17 * * 1-5". '
|
|
323
|
-
+ 'When both day fields are restricted, a date matches if either one matches.',
|
|
324
|
-
},
|
|
325
|
-
time_zone: { type: 'string', required: true, description: 'UTC or IANA Area/Location, for example Asia/Shanghai.' },
|
|
326
|
-
},
|
|
327
|
-
},
|
|
328
|
-
at: {
|
|
329
|
-
description: 'Absolute target: an RFC 3339 date-time with offset, or a local date, time, and IANA time_zone.',
|
|
330
|
-
oneOf: [
|
|
331
|
-
{ type: 'string' },
|
|
332
|
-
{
|
|
333
|
-
type: 'object',
|
|
334
|
-
additionalProperties: false,
|
|
335
|
-
properties: {
|
|
336
|
-
date: { type: 'string', required: true },
|
|
337
|
-
time: { type: 'string', required: true },
|
|
338
|
-
time_zone: { type: 'string', required: true },
|
|
339
|
-
},
|
|
340
|
-
},
|
|
341
|
-
],
|
|
342
|
-
},
|
|
343
|
-
};
|
|
344
|
-
/**
|
|
345
|
-
* Register all four Schedule tools in one exact agent scope.
|
|
346
|
-
* @param rootCtx - Host context owning the shared Schedule service.
|
|
347
|
-
* @param toolCtx - Exact agent-scoped context receiving the definitions.
|
|
348
|
-
* @param agent - Exact live owner whose session the tools mutate.
|
|
349
|
-
* @returns Idempotent aggregate disposer for the four registrations.
|
|
350
|
-
*/
|
|
351
|
-
export function registerScheduleTools(rootCtx, toolCtx, agent) {
|
|
352
|
-
const disposers = [];
|
|
353
|
-
try {
|
|
354
|
-
disposers.push(toolCtx.tools.register(defineTool({
|
|
355
|
-
name: 'schedule_create',
|
|
356
|
-
description: CREATE_DESCRIPTION,
|
|
357
|
-
parameters: {
|
|
358
|
-
prompt: {
|
|
359
|
-
type: 'string',
|
|
360
|
-
required: true,
|
|
361
|
-
description: 'Reminder content to present when the target becomes due.',
|
|
362
|
-
},
|
|
363
|
-
title: {
|
|
364
|
-
type: 'string',
|
|
365
|
-
required: true,
|
|
366
|
-
description: `Task name of at most ${MAX_TITLE_LENGTH} characters, shown on the task card and in task lists.`,
|
|
367
|
-
},
|
|
368
|
-
after_seconds: {
|
|
369
|
-
type: 'number',
|
|
370
|
-
description: 'Delay in whole seconds.',
|
|
371
|
-
},
|
|
372
|
-
...SELECTOR_PARAMETERS,
|
|
373
|
-
},
|
|
374
|
-
output: { schema: CREATE_OUTPUT_SCHEMA, render: renderValue },
|
|
375
|
-
async execute(args, exec) {
|
|
376
|
-
if (exec.agent !== agent)
|
|
377
|
-
return internalError();
|
|
378
|
-
const invalid = validateCreateArgs(args);
|
|
379
|
-
if (invalid !== undefined)
|
|
380
|
-
return invalid;
|
|
381
|
-
if (exec.signal.aborted)
|
|
382
|
-
return internalError();
|
|
383
|
-
try {
|
|
384
|
-
return scheduleView(await rootCtx.schedule.create(agent.session.id, args, exec.signal), Date.now());
|
|
385
|
-
}
|
|
386
|
-
catch (error) {
|
|
387
|
-
return operationError(error);
|
|
388
|
-
}
|
|
389
|
-
},
|
|
390
|
-
presentCall: args => present('Create reminder', 'other', args.prompt),
|
|
391
|
-
})));
|
|
392
|
-
disposers.push(toolCtx.tools.register(defineTool({
|
|
393
|
-
name: 'schedule_list',
|
|
394
|
-
description: LIST_DESCRIPTION,
|
|
395
|
-
parameters: {},
|
|
396
|
-
output: { schema: LIST_OUTPUT_SCHEMA, render: renderValue },
|
|
397
|
-
async execute(_args, exec) {
|
|
398
|
-
if (exec.agent !== agent)
|
|
399
|
-
return internalError();
|
|
400
|
-
if (exec.signal.aborted)
|
|
401
|
-
return internalError();
|
|
402
|
-
try {
|
|
403
|
-
const records = await rootCtx.schedule.list({ sessionId: agent.session.id });
|
|
404
|
-
return records.map(record => scheduleView(record, Date.now()));
|
|
405
|
-
}
|
|
406
|
-
catch (error) {
|
|
407
|
-
return operationError(error);
|
|
408
|
-
}
|
|
409
|
-
},
|
|
410
|
-
presentCall: () => present('List reminders', 'read'),
|
|
411
|
-
})));
|
|
412
|
-
disposers.push(toolCtx.tools.register(defineTool({
|
|
413
|
-
name: 'schedule_delete',
|
|
414
|
-
description: DELETE_DESCRIPTION,
|
|
415
|
-
parameters: {
|
|
416
|
-
id: { type: 'string', required: true, description: 'Schedule id returned by schedule_list.' },
|
|
417
|
-
},
|
|
418
|
-
output: { schema: DELETE_OUTPUT_SCHEMA, render: renderValue },
|
|
419
|
-
async execute(args, exec) {
|
|
420
|
-
if (args.id.length === 0 || args.id.trim() !== args.id) {
|
|
421
|
-
return { code: 'invalid_rule', message: 'schedule_delete id must be non-empty without surrounding whitespace.' };
|
|
422
|
-
}
|
|
423
|
-
const id = ScheduleId(args.id);
|
|
424
|
-
if (exec.agent !== agent)
|
|
425
|
-
return internalError();
|
|
426
|
-
if (exec.signal.aborted)
|
|
427
|
-
return internalError();
|
|
428
|
-
try {
|
|
429
|
-
return await rootCtx.schedule.delete({ sessionId: agent.session.id, id }, exec.signal);
|
|
430
|
-
}
|
|
431
|
-
catch (error) {
|
|
432
|
-
return operationError(error);
|
|
433
|
-
}
|
|
434
|
-
},
|
|
435
|
-
presentCall: args => present('Delete reminder', 'other', args.id),
|
|
436
|
-
})));
|
|
437
|
-
disposers.push(toolCtx.tools.register(defineTool({
|
|
438
|
-
name: 'schedule_update',
|
|
439
|
-
description: UPDATE_DESCRIPTION,
|
|
440
|
-
parameters: {
|
|
441
|
-
id: { type: 'string', required: true, description: 'Schedule id returned by schedule_list.' },
|
|
442
|
-
title: {
|
|
443
|
-
type: 'string',
|
|
444
|
-
description: `New task name of at most ${MAX_TITLE_LENGTH} characters.`,
|
|
445
|
-
},
|
|
446
|
-
prompt: {
|
|
447
|
-
type: 'string',
|
|
448
|
-
description: 'New reminder content.',
|
|
449
|
-
},
|
|
450
|
-
...SELECTOR_PARAMETERS,
|
|
451
|
-
},
|
|
452
|
-
output: { schema: UPDATE_OUTPUT_SCHEMA, render: renderValue },
|
|
453
|
-
async execute(args, exec) {
|
|
454
|
-
if (exec.agent !== agent)
|
|
455
|
-
return internalError();
|
|
456
|
-
const invalid = validateUpdateArgs(args);
|
|
457
|
-
if (invalid !== undefined)
|
|
458
|
-
return invalid;
|
|
459
|
-
if (exec.signal.aborted)
|
|
460
|
-
return internalError();
|
|
461
|
-
const id = ScheduleId(args.id);
|
|
462
|
-
try {
|
|
463
|
-
const sessionId = agent.session.id;
|
|
464
|
-
const expected = (await rootCtx.schedule.list({ sessionId }))
|
|
465
|
-
.find(record => record.id === id);
|
|
466
|
-
if (expected === undefined) {
|
|
467
|
-
// The catalog also holds inactive reminders, which is the one not-found
|
|
468
|
-
// case the model can act on: it has to create a new reminder instead.
|
|
469
|
-
const ended = (await rootCtx.schedule.catalog())
|
|
470
|
-
.some(entry => entry.sessionId === sessionId && entry.id === id);
|
|
471
|
-
return { id, updated: false, code: ended ? 'schedule_ended' : 'schedule_not_found' };
|
|
472
|
-
}
|
|
473
|
-
const change = timingChangeFrom(args);
|
|
474
|
-
const result = await rootCtx.schedule.update({
|
|
475
|
-
sessionId,
|
|
476
|
-
id,
|
|
477
|
-
expected,
|
|
478
|
-
...(change === undefined ? {} : { change }),
|
|
479
|
-
...(args.title === undefined ? {} : { title: args.title }),
|
|
480
|
-
...(args.prompt === undefined ? {} : { prompt: args.prompt }),
|
|
481
|
-
}, exec.signal);
|
|
482
|
-
return 'record' in result ? scheduleView(result.record, Date.now()) : result;
|
|
483
|
-
}
|
|
484
|
-
catch (error) {
|
|
485
|
-
return operationError(error);
|
|
486
|
-
}
|
|
487
|
-
},
|
|
488
|
-
presentCall: args => present('Update reminder', 'other', args.id),
|
|
489
|
-
})));
|
|
490
|
-
}
|
|
491
|
-
catch (error) {
|
|
492
|
-
for (const dispose of disposers.reverse())
|
|
493
|
-
dispose();
|
|
494
|
-
throw error;
|
|
495
|
-
}
|
|
496
|
-
let active = true;
|
|
497
|
-
return () => {
|
|
498
|
-
if (!active)
|
|
499
|
-
return;
|
|
500
|
-
active = false;
|
|
501
|
-
for (const dispose of disposers.reverse())
|
|
502
|
-
dispose();
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
//# sourceMappingURL=tools.js.map
|