@deepseek-ai/dsh-tool-schedule 0.2.1-rc.5 → 0.2.2-alpha.3
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 +50 -32
- package/README.md +47 -20
- package/README.zh.md +47 -20
- package/lib/index.js +477 -445
- package/lib/types/index.d.ts +9 -8
- package/locale/de.json +6 -0
- package/locale/en.json +6 -0
- package/locale/es.json +6 -0
- package/locale/fr.json +6 -0
- package/locale/ja.json +6 -0
- package/locale/pt.json +6 -0
- package/locale/ru.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +23 -15
package/lib/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { delegationDepthOf } from "@deepseek-ai/dsh-subagent";
|
|
2
2
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
3
|
-
import { MAX_TITLE_LENGTH, MIN_EVERY_INTERVAL_SECONDS,
|
|
3
|
+
import { DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT, MAX_TITLE_LENGTH, MIN_EVERY_INTERVAL_SECONDS, ScheduleId, ScheduleInputError, formatScheduleLocalTime } from "@deepseek-ai/dsh-schedule";
|
|
4
4
|
//#region lib/types/index.js
|
|
5
5
|
/**
|
|
6
|
-
* Model-facing `schedule_create`, `schedule_list`, `
|
|
7
|
-
* `
|
|
6
|
+
* Model-facing `schedule_create`, `schedule_list`, `schedule_view`, and
|
|
7
|
+
* `schedule_edit` tools over the Host `ctx.schedule` service. Mounting the
|
|
8
8
|
* plugin registers them in the mounting scope, so a preset decides which
|
|
9
|
-
* agents receive them
|
|
9
|
+
* agents receive them. Tasks belong to the Host, not to a Session: the tools
|
|
10
|
+
* read and change any task by id, and only the delivery target names a Session.
|
|
10
11
|
* @module @deepseek-ai/dsh-tool-schedule
|
|
11
12
|
*/
|
|
12
13
|
/** Plugin name registered with the Loader. */
|
|
@@ -14,48 +15,27 @@ const name = "tool-schedule";
|
|
|
14
15
|
/** Host services this plugin consumes. The Schedule service is resolved per
|
|
15
16
|
* scope, so a preset that declares this plugin stays inert while the shipped
|
|
16
17
|
* composition keeps that service off. */
|
|
17
|
-
const inject = ["tools"];
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
type: "string",
|
|
25
|
-
required: true
|
|
26
|
-
},
|
|
27
|
-
prompt: {
|
|
28
|
-
type: "string",
|
|
29
|
-
required: true
|
|
30
|
-
},
|
|
31
|
-
scheduledAt: {
|
|
32
|
-
type: "string",
|
|
33
|
-
required: true
|
|
34
|
-
},
|
|
35
|
-
state: {
|
|
36
|
-
type: "string",
|
|
37
|
-
required: true,
|
|
38
|
-
enum: ["scheduled", "overdue"]
|
|
39
|
-
},
|
|
40
|
-
deliveryMode: {
|
|
41
|
-
type: "string",
|
|
42
|
-
required: true,
|
|
43
|
-
const: "host"
|
|
18
|
+
const inject = ["tools", "systemPrompt"];
|
|
19
|
+
const SESSION_CREATE_SCHEMA = {
|
|
20
|
+
type: "object",
|
|
21
|
+
additionalProperties: false,
|
|
22
|
+
properties: {
|
|
23
|
+
cwd: { type: "string" },
|
|
24
|
+
agentPreset: { type: "string" }
|
|
44
25
|
}
|
|
45
26
|
};
|
|
46
|
-
const
|
|
27
|
+
const TARGET_SCHEMA = { oneOf: [
|
|
47
28
|
{
|
|
48
29
|
type: "object",
|
|
49
30
|
additionalProperties: false,
|
|
50
31
|
properties: {
|
|
51
|
-
...SHARED_VIEW_PROPERTIES,
|
|
52
32
|
kind: {
|
|
53
33
|
type: "string",
|
|
54
34
|
required: true,
|
|
55
|
-
const: "
|
|
35
|
+
const: "session"
|
|
56
36
|
},
|
|
57
|
-
|
|
58
|
-
type: "
|
|
37
|
+
sessionId: {
|
|
38
|
+
type: "string",
|
|
59
39
|
required: true
|
|
60
40
|
}
|
|
61
41
|
}
|
|
@@ -64,11 +44,18 @@ const VIEW_SCHEMA = { oneOf: [
|
|
|
64
44
|
type: "object",
|
|
65
45
|
additionalProperties: false,
|
|
66
46
|
properties: {
|
|
67
|
-
...SHARED_VIEW_PROPERTIES,
|
|
68
47
|
kind: {
|
|
69
48
|
type: "string",
|
|
70
49
|
required: true,
|
|
71
|
-
const: "
|
|
50
|
+
const: "new_once"
|
|
51
|
+
},
|
|
52
|
+
sessionId: {
|
|
53
|
+
type: "string",
|
|
54
|
+
required: true
|
|
55
|
+
},
|
|
56
|
+
create: {
|
|
57
|
+
...SESSION_CREATE_SCHEMA,
|
|
58
|
+
required: true
|
|
72
59
|
}
|
|
73
60
|
}
|
|
74
61
|
},
|
|
@@ -76,189 +63,345 @@ const VIEW_SCHEMA = { oneOf: [
|
|
|
76
63
|
type: "object",
|
|
77
64
|
additionalProperties: false,
|
|
78
65
|
properties: {
|
|
79
|
-
...SHARED_VIEW_PROPERTIES,
|
|
80
66
|
kind: {
|
|
81
67
|
type: "string",
|
|
82
68
|
required: true,
|
|
83
|
-
const: "
|
|
69
|
+
const: "new"
|
|
84
70
|
},
|
|
85
|
-
|
|
86
|
-
|
|
71
|
+
create: {
|
|
72
|
+
...SESSION_CREATE_SCHEMA,
|
|
87
73
|
required: true
|
|
88
74
|
}
|
|
89
75
|
}
|
|
90
|
-
}
|
|
76
|
+
}
|
|
77
|
+
] };
|
|
78
|
+
const DELIVERY_SCHEMA = {
|
|
79
|
+
type: "object",
|
|
80
|
+
additionalProperties: false,
|
|
81
|
+
properties: {
|
|
82
|
+
scheduledAt: {
|
|
83
|
+
type: "string",
|
|
84
|
+
required: true
|
|
85
|
+
},
|
|
86
|
+
deliveredAt: {
|
|
87
|
+
type: "string",
|
|
88
|
+
required: true
|
|
89
|
+
},
|
|
90
|
+
targetSessionId: {
|
|
91
|
+
type: "string",
|
|
92
|
+
required: true
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const TASK_PROPERTIES = {
|
|
97
|
+
type: "object",
|
|
98
|
+
additionalProperties: false,
|
|
99
|
+
properties: {
|
|
100
|
+
id: {
|
|
101
|
+
type: "string",
|
|
102
|
+
required: true
|
|
103
|
+
},
|
|
104
|
+
title: {
|
|
105
|
+
type: "string",
|
|
106
|
+
required: true
|
|
107
|
+
},
|
|
108
|
+
prompt: {
|
|
109
|
+
type: "string",
|
|
110
|
+
required: true
|
|
111
|
+
},
|
|
112
|
+
scheduledAt: {
|
|
113
|
+
type: "string",
|
|
114
|
+
required: true
|
|
115
|
+
},
|
|
116
|
+
scheduledAtLocal: {
|
|
117
|
+
type: "string",
|
|
118
|
+
required: true
|
|
119
|
+
},
|
|
120
|
+
target: {
|
|
121
|
+
...TARGET_SCHEMA,
|
|
122
|
+
required: true
|
|
123
|
+
},
|
|
124
|
+
status: {
|
|
125
|
+
type: "string",
|
|
126
|
+
required: true,
|
|
127
|
+
enum: [
|
|
128
|
+
"active",
|
|
129
|
+
"paused",
|
|
130
|
+
"ended"
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
updatedAt: { type: "string" },
|
|
134
|
+
lastDelivery: DELIVERY_SCHEMA
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const TASK_SCHEMA = { oneOf: [
|
|
91
138
|
{
|
|
92
|
-
|
|
93
|
-
additionalProperties: false,
|
|
139
|
+
...TASK_PROPERTIES,
|
|
94
140
|
properties: {
|
|
95
|
-
...
|
|
141
|
+
...TASK_PROPERTIES.properties,
|
|
96
142
|
kind: {
|
|
97
143
|
type: "string",
|
|
98
144
|
required: true,
|
|
99
|
-
const: "
|
|
145
|
+
const: "rrule"
|
|
100
146
|
},
|
|
101
|
-
|
|
102
|
-
type: "string",
|
|
103
|
-
required: true
|
|
104
|
-
},
|
|
105
|
-
timeZone: {
|
|
147
|
+
rrule: {
|
|
106
148
|
type: "string",
|
|
107
149
|
required: true
|
|
108
150
|
}
|
|
109
151
|
}
|
|
110
152
|
},
|
|
111
153
|
{
|
|
112
|
-
|
|
113
|
-
additionalProperties: false,
|
|
154
|
+
...TASK_PROPERTIES,
|
|
114
155
|
properties: {
|
|
115
|
-
...
|
|
156
|
+
...TASK_PROPERTIES.properties,
|
|
116
157
|
kind: {
|
|
117
158
|
type: "string",
|
|
118
159
|
required: true,
|
|
119
|
-
const: "
|
|
160
|
+
const: "cron"
|
|
120
161
|
},
|
|
121
|
-
|
|
162
|
+
expression: {
|
|
122
163
|
type: "string",
|
|
123
164
|
required: true
|
|
124
165
|
},
|
|
125
166
|
timeZone: {
|
|
126
167
|
type: "string",
|
|
127
168
|
required: true
|
|
128
|
-
},
|
|
129
|
-
weekdays: {
|
|
130
|
-
type: "array",
|
|
131
|
-
required: true,
|
|
132
|
-
items: { type: "integer" }
|
|
133
169
|
}
|
|
134
170
|
}
|
|
135
171
|
},
|
|
136
172
|
{
|
|
137
|
-
|
|
138
|
-
additionalProperties: false,
|
|
173
|
+
...TASK_PROPERTIES,
|
|
139
174
|
properties: {
|
|
140
|
-
...
|
|
175
|
+
...TASK_PROPERTIES.properties,
|
|
141
176
|
kind: {
|
|
142
177
|
type: "string",
|
|
143
178
|
required: true,
|
|
144
|
-
const: "
|
|
145
|
-
},
|
|
146
|
-
expression: {
|
|
147
|
-
type: "string",
|
|
148
|
-
required: true
|
|
179
|
+
const: "every"
|
|
149
180
|
},
|
|
150
|
-
|
|
151
|
-
type: "
|
|
181
|
+
everySeconds: {
|
|
182
|
+
type: "integer",
|
|
152
183
|
required: true
|
|
153
184
|
}
|
|
154
185
|
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
function basicErrorSchema(code) {
|
|
159
|
-
return {
|
|
160
|
-
type: "object",
|
|
161
|
-
additionalProperties: false,
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
...TASK_PROPERTIES,
|
|
162
189
|
properties: {
|
|
163
|
-
|
|
190
|
+
...TASK_PROPERTIES.properties,
|
|
191
|
+
kind: {
|
|
164
192
|
type: "string",
|
|
165
193
|
required: true,
|
|
166
|
-
const:
|
|
194
|
+
const: "after"
|
|
167
195
|
},
|
|
168
|
-
|
|
169
|
-
type: "
|
|
196
|
+
afterSeconds: {
|
|
197
|
+
type: "integer",
|
|
170
198
|
required: true
|
|
171
199
|
}
|
|
172
200
|
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
const ERROR_SCHEMAS = [
|
|
176
|
-
basicErrorSchema("invalid_prompt"),
|
|
177
|
-
basicErrorSchema("invalid_selector"),
|
|
178
|
-
basicErrorSchema("invalid_rule"),
|
|
179
|
-
basicErrorSchema("invalid_time_zone"),
|
|
180
|
-
basicErrorSchema("not_future"),
|
|
181
|
-
basicErrorSchema("time_out_of_range"),
|
|
182
|
-
basicErrorSchema("frequency_too_high"),
|
|
183
|
-
basicErrorSchema("subagent_session"),
|
|
184
|
-
basicErrorSchema("internal_error")
|
|
185
|
-
];
|
|
186
|
-
const CREATE_OUTPUT_SCHEMA = { oneOf: [VIEW_SCHEMA, ...ERROR_SCHEMAS] };
|
|
187
|
-
const LIST_OUTPUT_SCHEMA = { oneOf: [{
|
|
188
|
-
type: "array",
|
|
189
|
-
items: VIEW_SCHEMA
|
|
190
|
-
}, ...ERROR_SCHEMAS] };
|
|
191
|
-
const DELETE_OUTPUT_SCHEMA = { oneOf: [
|
|
201
|
+
},
|
|
192
202
|
{
|
|
193
|
-
|
|
194
|
-
additionalProperties: false,
|
|
203
|
+
...TASK_PROPERTIES,
|
|
195
204
|
properties: {
|
|
196
|
-
|
|
205
|
+
...TASK_PROPERTIES.properties,
|
|
206
|
+
kind: {
|
|
197
207
|
type: "string",
|
|
198
|
-
required: true
|
|
199
|
-
},
|
|
200
|
-
deleted: {
|
|
201
|
-
type: "boolean",
|
|
202
208
|
required: true,
|
|
203
|
-
const:
|
|
209
|
+
const: "at"
|
|
204
210
|
}
|
|
205
211
|
}
|
|
206
212
|
},
|
|
207
213
|
{
|
|
208
|
-
|
|
209
|
-
additionalProperties: false,
|
|
214
|
+
...TASK_PROPERTIES,
|
|
210
215
|
properties: {
|
|
211
|
-
|
|
216
|
+
...TASK_PROPERTIES.properties,
|
|
217
|
+
kind: {
|
|
218
|
+
type: "string",
|
|
219
|
+
required: true,
|
|
220
|
+
const: "daily"
|
|
221
|
+
},
|
|
222
|
+
time: {
|
|
212
223
|
type: "string",
|
|
213
224
|
required: true
|
|
214
225
|
},
|
|
215
|
-
|
|
216
|
-
type: "
|
|
226
|
+
timeZone: {
|
|
227
|
+
type: "string",
|
|
228
|
+
required: true
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
...TASK_PROPERTIES,
|
|
234
|
+
properties: {
|
|
235
|
+
...TASK_PROPERTIES.properties,
|
|
236
|
+
kind: {
|
|
237
|
+
type: "string",
|
|
217
238
|
required: true,
|
|
218
|
-
const:
|
|
239
|
+
const: "weekly"
|
|
219
240
|
},
|
|
220
|
-
|
|
241
|
+
time: {
|
|
221
242
|
type: "string",
|
|
243
|
+
required: true
|
|
244
|
+
},
|
|
245
|
+
timeZone: {
|
|
246
|
+
type: "string",
|
|
247
|
+
required: true
|
|
248
|
+
},
|
|
249
|
+
weekdays: {
|
|
250
|
+
type: "array",
|
|
222
251
|
required: true,
|
|
223
|
-
|
|
252
|
+
items: { type: "integer" }
|
|
224
253
|
}
|
|
225
254
|
}
|
|
226
|
-
}
|
|
227
|
-
...ERROR_SCHEMAS
|
|
255
|
+
}
|
|
228
256
|
] };
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
257
|
+
const LIST_PAGE_SCHEMA = {
|
|
258
|
+
type: "object",
|
|
259
|
+
additionalProperties: false,
|
|
260
|
+
properties: {
|
|
261
|
+
items: {
|
|
262
|
+
type: "array",
|
|
263
|
+
required: true,
|
|
264
|
+
items: TASK_SCHEMA
|
|
265
|
+
},
|
|
266
|
+
shown: {
|
|
267
|
+
type: "integer",
|
|
268
|
+
required: true
|
|
269
|
+
},
|
|
270
|
+
total: {
|
|
271
|
+
type: "integer",
|
|
272
|
+
required: true
|
|
273
|
+
},
|
|
274
|
+
limit: {
|
|
275
|
+
type: "integer",
|
|
276
|
+
required: true
|
|
277
|
+
},
|
|
278
|
+
offset: {
|
|
279
|
+
type: "integer",
|
|
280
|
+
required: true
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
const NOT_FOUND_SCHEMA = {
|
|
285
|
+
type: "object",
|
|
286
|
+
additionalProperties: false,
|
|
287
|
+
properties: {
|
|
288
|
+
id: {
|
|
289
|
+
type: "string",
|
|
290
|
+
required: true
|
|
291
|
+
},
|
|
292
|
+
code: {
|
|
293
|
+
type: "string",
|
|
294
|
+
required: true,
|
|
295
|
+
const: "schedule_not_found"
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
const DELETED_SCHEMA = {
|
|
300
|
+
type: "object",
|
|
301
|
+
additionalProperties: false,
|
|
302
|
+
properties: {
|
|
303
|
+
id: {
|
|
304
|
+
type: "string",
|
|
305
|
+
required: true
|
|
306
|
+
},
|
|
307
|
+
deleted: {
|
|
308
|
+
type: "boolean",
|
|
309
|
+
required: true,
|
|
310
|
+
const: true
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
const CONFLICT_SCHEMA = {
|
|
315
|
+
type: "object",
|
|
316
|
+
additionalProperties: false,
|
|
317
|
+
properties: {
|
|
318
|
+
id: {
|
|
319
|
+
type: "string",
|
|
320
|
+
required: true
|
|
321
|
+
},
|
|
322
|
+
code: {
|
|
323
|
+
type: "string",
|
|
324
|
+
required: true,
|
|
325
|
+
const: "schedule_conflict"
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
/** Errors every tool can return: a delegated caller and a withheld failure. */
|
|
330
|
+
const CALLER_ERRORS = ["subagent_session", "internal_error"];
|
|
331
|
+
/** Errors of a new title, prompt, or recurrence. */
|
|
332
|
+
const CREATE_ERRORS = [
|
|
333
|
+
"invalid_prompt",
|
|
334
|
+
"invalid_rule",
|
|
335
|
+
"invalid_time_zone",
|
|
336
|
+
"not_future",
|
|
337
|
+
"frequency_too_high",
|
|
338
|
+
...CALLER_ERRORS
|
|
339
|
+
];
|
|
340
|
+
/** Errors of an invalid page request. */
|
|
341
|
+
const LIST_ERRORS = ["invalid_rule", ...CALLER_ERRORS];
|
|
342
|
+
/** Errors of a changed field or an invalid field combination. */
|
|
343
|
+
const EDIT_ERRORS = ["invalid_selector", ...CREATE_ERRORS];
|
|
344
|
+
/** Build the exact two-field error schema of one tool from the codes it can return. */
|
|
345
|
+
function errorSchema(codes) {
|
|
346
|
+
return {
|
|
232
347
|
type: "object",
|
|
233
348
|
additionalProperties: false,
|
|
234
349
|
properties: {
|
|
235
|
-
|
|
350
|
+
code: {
|
|
236
351
|
type: "string",
|
|
237
|
-
required: true
|
|
238
|
-
},
|
|
239
|
-
updated: {
|
|
240
|
-
type: "boolean",
|
|
241
352
|
required: true,
|
|
242
|
-
|
|
353
|
+
enum: codes
|
|
243
354
|
},
|
|
244
|
-
|
|
355
|
+
message: {
|
|
245
356
|
type: "string",
|
|
246
|
-
required: true
|
|
247
|
-
enum: [
|
|
248
|
-
"schedule_not_found",
|
|
249
|
-
"schedule_ended",
|
|
250
|
-
"schedule_conflict"
|
|
251
|
-
]
|
|
357
|
+
required: true
|
|
252
358
|
}
|
|
253
359
|
}
|
|
254
|
-
}
|
|
255
|
-
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
const CREATE_OUTPUT_SCHEMA = { oneOf: [TASK_SCHEMA, errorSchema(CREATE_ERRORS)] };
|
|
363
|
+
const LIST_OUTPUT_SCHEMA = { oneOf: [LIST_PAGE_SCHEMA, errorSchema(LIST_ERRORS)] };
|
|
364
|
+
const VIEW_OUTPUT_SCHEMA = { oneOf: [
|
|
365
|
+
TASK_SCHEMA,
|
|
366
|
+
NOT_FOUND_SCHEMA,
|
|
367
|
+
errorSchema(CALLER_ERRORS)
|
|
368
|
+
] };
|
|
369
|
+
const EDIT_OUTPUT_SCHEMA = { oneOf: [
|
|
370
|
+
TASK_SCHEMA,
|
|
371
|
+
DELETED_SCHEMA,
|
|
372
|
+
NOT_FOUND_SCHEMA,
|
|
373
|
+
CONFLICT_SCHEMA,
|
|
374
|
+
errorSchema(EDIT_ERRORS)
|
|
256
375
|
] };
|
|
257
|
-
const
|
|
258
|
-
const
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
const
|
|
376
|
+
const MANAGEMENT_AUTHORIZATION = "Only create or modify scheduled tasks at the user's explicit request; prior explicit user authorization for the running scheduled task remains valid.";
|
|
377
|
+
const CREATE_DESCRIPTION = "Create a scheduled task. At each occurrence of rrule, prompt is delivered to the target session as a scheduled message from the user. The result includes the task id and the next occurrence as scheduledAt in UTC and scheduledAtLocal in the zone of rrule. After downtime, only the latest missed occurrence is delivered. Delivery can repeat after a crash.";
|
|
378
|
+
const LIST_DESCRIPTION = "List scheduled tasks, including tasks created from other sessions. Active tasks come first, ordered by scheduledAt.";
|
|
379
|
+
const VIEW_DESCRIPTION = "View one scheduled task by id.";
|
|
380
|
+
const EDIT_DESCRIPTION = "Update, pause, resume, or delete one scheduled task by id. Deleting does not retract queued messages.";
|
|
381
|
+
const TITLE_HELP = `Task name of at most ${MAX_TITLE_LENGTH} characters in the user's language: action plus object. Omit timing, status, ids, and bracketed suffixes.`;
|
|
382
|
+
const RRULE_HELP = "Supported RFC 5545 subset: one DTSTART line, a newline, and one RRULE line. DTSTART ends in Z for UTC or carries TZID with an IANA time zone; a fixed offset such as UTC+5 is Etc/GMT-5, with the sign reversed. DTSTART must name an existing local time. A TZID rule keeps local clock time across daylight-saving changes; generated nonexistent local times are skipped, and repeated ones occur once, at the earlier instant. DTSTART may be in the past; delivery starts with the first occurrence after creation. A one-time delivery is RRULE:FREQ=DAILY;COUNT=1. No SECONDLY; BYSECOND takes one value. COUNT is at most 1000; use UNTIL for longer rules. MINUTELY and HOURLY accept no BY parts, so select clock times with DAILY or WEEKLY plus BYHOUR and BYMINUTE. BYMONTHDAY and ordinal BYDAY require MONTHLY or YEARLY; BYYEARDAY and BYWEEKNO require YEARLY. Ordinal BYDAY cannot combine with BYWEEKNO. Each FREQ period allows at most 10080 candidates before BYSETPOS, which accepts at most 31 values. UNTIL is a UTC instant ending in Z. Example: \"DTSTART;TZID=Asia/Singapore:20260901T080000\\nRRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1\"";
|
|
383
|
+
const TARGET_CHOICES = [
|
|
384
|
+
"this_session",
|
|
385
|
+
"new_once",
|
|
386
|
+
"new"
|
|
387
|
+
];
|
|
388
|
+
/**
|
|
389
|
+
* Describe the target choices, naming the configured spacing of `new` deliveries when it exceeds the one-minute
|
|
390
|
+
* spacing of every task.
|
|
391
|
+
* @param newSessionIntervalSeconds - Effective `newSessionIntervalSeconds` of the Host Schedule service.
|
|
392
|
+
* @returns The `target` parameter description.
|
|
393
|
+
*/
|
|
394
|
+
function targetHelp(newSessionIntervalSeconds) {
|
|
395
|
+
return `this_session delivers to this session; new_once delivers to the same new session every time, opened at the first delivery; new opens a different new session for every delivery${newSessionIntervalSeconds <= MIN_EVERY_INTERVAL_SECONDS ? "" : newSessionIntervalSeconds % 60 === 0 ? `, at most once every ${newSessionIntervalSeconds / 60} minutes` : `, at most once every ${newSessionIntervalSeconds} seconds`}. New sessions use this session's working directory and preset. With this_session or new_once, a delivery waits until the previous one finishes, and only the latest occurrence that passed meanwhile is delivered. Use this_session for reminders and for follow-ups to this conversation; use new_once or new for independent work, such as a recurring report, and whenever the user asks for a new conversation.`;
|
|
396
|
+
}
|
|
397
|
+
/** Edit parameters that only an update accepts. */
|
|
398
|
+
const PATCH_FIELDS = [
|
|
399
|
+
"title",
|
|
400
|
+
"prompt",
|
|
401
|
+
"rrule",
|
|
402
|
+
"target",
|
|
403
|
+
"active"
|
|
404
|
+
];
|
|
262
405
|
/** Deterministic model content for every canonical Schedule value. */
|
|
263
406
|
function renderValue(_args, value) {
|
|
264
407
|
return [{
|
|
@@ -266,6 +409,19 @@ function renderValue(_args, value) {
|
|
|
266
409
|
text: JSON.stringify(value)
|
|
267
410
|
}];
|
|
268
411
|
}
|
|
412
|
+
/** Preserve timing, destination, and edit revision while withholding Host bookkeeping. */
|
|
413
|
+
function modelTask(task) {
|
|
414
|
+
const { involvedSessionIds: _involved, createdAt: _created, executionActive: _execution, deliveryFloorEndsAt: _floor, lastDelivery, ...record } = task;
|
|
415
|
+
return {
|
|
416
|
+
...record,
|
|
417
|
+
scheduledAtLocal: formatScheduleLocalTime(task, task.scheduledAt),
|
|
418
|
+
...lastDelivery === void 0 ? {} : { lastDelivery: {
|
|
419
|
+
scheduledAt: lastDelivery.scheduledAt,
|
|
420
|
+
deliveredAt: lastDelivery.deliveredAt,
|
|
421
|
+
targetSessionId: lastDelivery.targetSessionId
|
|
422
|
+
} }
|
|
423
|
+
};
|
|
424
|
+
}
|
|
269
425
|
/** Pure generic pending card. */
|
|
270
426
|
function present(title, kind, rawInput) {
|
|
271
427
|
return {
|
|
@@ -282,379 +438,255 @@ function internalError() {
|
|
|
282
438
|
message: "The schedule operation failed."
|
|
283
439
|
};
|
|
284
440
|
}
|
|
285
|
-
/** Translate invalid input while withholding internal storage failures. */
|
|
286
|
-
function operationError(error) {
|
|
287
|
-
return error instanceof ScheduleInputError ? {
|
|
288
|
-
code: error.code,
|
|
289
|
-
message: error.message
|
|
290
|
-
} : internalError();
|
|
291
|
-
}
|
|
292
441
|
/**
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
* @
|
|
442
|
+
* Run one Schedule operation for the root Agent that dispatched the call.
|
|
443
|
+
*
|
|
444
|
+
* A delegated subagent cannot use scheduled tasks, so every tool refuses it here even when its preset exposes the
|
|
445
|
+
* tool. An input error keeps its code when the tool declares that code; every other failure becomes `internal_error`.
|
|
446
|
+
* @param exec - Run context of the call.
|
|
447
|
+
* @param codes - Error codes of the tool.
|
|
448
|
+
* @param operation - Service call for the calling Agent.
|
|
449
|
+
* @returns The operation result, or an error with a declared code.
|
|
298
450
|
*/
|
|
299
|
-
function
|
|
300
|
-
|
|
301
|
-
return
|
|
451
|
+
async function runForCaller(exec, codes, operation) {
|
|
452
|
+
const agent = exec.agent;
|
|
453
|
+
if (agent === void 0) return internalError();
|
|
454
|
+
if (delegationDepthOf(agent) > 0) return {
|
|
302
455
|
code: "subagent_session",
|
|
303
|
-
message: "A delegated subagent cannot use
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
/** One supplied fixed-rate interval: a safe integer at or above the Host floor, or undefined. */
|
|
307
|
-
function invalidInterval(everySeconds) {
|
|
308
|
-
if (everySeconds === void 0) return void 0;
|
|
309
|
-
if (!Number.isSafeInteger(everySeconds)) return {
|
|
310
|
-
code: "invalid_rule",
|
|
311
|
-
message: "every_seconds must be a safe integer."
|
|
312
|
-
};
|
|
313
|
-
if (everySeconds < MIN_EVERY_INTERVAL_SECONDS) return {
|
|
314
|
-
code: "frequency_too_high",
|
|
315
|
-
message: `every_seconds must be at least ${MIN_EVERY_INTERVAL_SECONDS}.`
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
/** Validate selector constraints that the open parameter root cannot express. */
|
|
319
|
-
function validateCreateArgs(args) {
|
|
320
|
-
if (Object.keys(args).some((key) => key !== "prompt" && key !== "title" && key !== "after_seconds" && key !== "at" && key !== "every_seconds" && key !== "daily" && key !== "weekly" && key !== "cron") || Number(args.after_seconds !== void 0) + Number(args.at !== void 0) + Number(args.every_seconds !== void 0) + Number(args.daily !== void 0) + Number(args.weekly !== void 0) + Number(args.cron !== void 0) !== 1) return {
|
|
321
|
-
code: "invalid_selector",
|
|
322
|
-
message: "schedule_create accepts exactly one of after_seconds, at, every_seconds, daily, weekly, or cron."
|
|
323
|
-
};
|
|
324
|
-
if (args.prompt.trim().length === 0) return {
|
|
325
|
-
code: "invalid_prompt",
|
|
326
|
-
message: "prompt must be non-empty after trimming."
|
|
327
|
-
};
|
|
328
|
-
if (args.title.trim().length === 0) return {
|
|
329
|
-
code: "invalid_prompt",
|
|
330
|
-
message: REQUIRED_TITLE_MESSAGE
|
|
331
|
-
};
|
|
332
|
-
if (args.title.trim().length > MAX_TITLE_LENGTH) return {
|
|
333
|
-
code: "invalid_prompt",
|
|
334
|
-
message: `title must be at most ${MAX_TITLE_LENGTH} characters.`
|
|
335
|
-
};
|
|
336
|
-
if (args.after_seconds !== void 0 && (!Number.isSafeInteger(args.after_seconds) || args.after_seconds <= 0)) return {
|
|
337
|
-
code: "invalid_rule",
|
|
338
|
-
message: "after_seconds must be a positive safe integer."
|
|
339
|
-
};
|
|
340
|
-
return invalidInterval(args.every_seconds);
|
|
341
|
-
}
|
|
342
|
-
/** Validate the in-place update's selector count, id, and any supplied name, instruction, or interval. */
|
|
343
|
-
function validateUpdateArgs(args) {
|
|
344
|
-
const selectors = [
|
|
345
|
-
args.at !== void 0,
|
|
346
|
-
args.every_seconds !== void 0,
|
|
347
|
-
args.daily !== void 0,
|
|
348
|
-
args.weekly !== void 0,
|
|
349
|
-
args.cron !== void 0
|
|
350
|
-
].filter(Boolean).length;
|
|
351
|
-
if (Object.keys(args).some((key) => key !== "id" && key !== "title" && key !== "prompt" && key !== "at" && key !== "every_seconds" && key !== "daily" && key !== "weekly" && key !== "cron") || selectors > 1) return {
|
|
352
|
-
code: "invalid_selector",
|
|
353
|
-
message: "schedule_update accepts at most one of at, every_seconds, daily, weekly, or cron."
|
|
354
|
-
};
|
|
355
|
-
if (args.id.length === 0 || args.id.trim() !== args.id) return {
|
|
356
|
-
code: "invalid_rule",
|
|
357
|
-
message: "schedule_update id must be non-empty without surrounding whitespace."
|
|
358
|
-
};
|
|
359
|
-
if (selectors === 0 && args.title === void 0 && args.prompt === void 0) return {
|
|
360
|
-
code: "invalid_selector",
|
|
361
|
-
message: "schedule_update needs a new title, prompt, or one of at, every_seconds, daily, weekly, or cron."
|
|
362
|
-
};
|
|
363
|
-
if (args.title !== void 0 && args.title.trim().length === 0) return {
|
|
364
|
-
code: "invalid_prompt",
|
|
365
|
-
message: REQUIRED_TITLE_MESSAGE
|
|
366
|
-
};
|
|
367
|
-
if (args.title !== void 0 && args.title.trim().length > MAX_TITLE_LENGTH) return {
|
|
368
|
-
code: "invalid_prompt",
|
|
369
|
-
message: `title must be at most ${MAX_TITLE_LENGTH} characters.`
|
|
370
|
-
};
|
|
371
|
-
if (args.prompt !== void 0 && args.prompt.trim().length === 0) return {
|
|
372
|
-
code: "invalid_prompt",
|
|
373
|
-
message: "prompt must be non-empty after trimming."
|
|
374
|
-
};
|
|
375
|
-
return invalidInterval(args.every_seconds);
|
|
376
|
-
}
|
|
377
|
-
/** The one timing replacement the update carries, or undefined when the request keeps the committed target. */
|
|
378
|
-
function timingChangeFrom(args) {
|
|
379
|
-
if (args.at !== void 0) return {
|
|
380
|
-
kind: "at",
|
|
381
|
-
at: args.at
|
|
382
|
-
};
|
|
383
|
-
if (args.every_seconds !== void 0) return {
|
|
384
|
-
kind: "every",
|
|
385
|
-
every_seconds: args.every_seconds
|
|
386
|
-
};
|
|
387
|
-
if (args.daily !== void 0) return {
|
|
388
|
-
kind: "daily",
|
|
389
|
-
daily: args.daily
|
|
390
|
-
};
|
|
391
|
-
if (args.weekly !== void 0) return {
|
|
392
|
-
kind: "weekly",
|
|
393
|
-
weekly: args.weekly
|
|
394
|
-
};
|
|
395
|
-
if (args.cron !== void 0) return {
|
|
396
|
-
kind: "cron",
|
|
397
|
-
cron: args.cron
|
|
456
|
+
message: "A delegated subagent cannot use scheduled tasks."
|
|
398
457
|
};
|
|
458
|
+
try {
|
|
459
|
+
return await operation(agent);
|
|
460
|
+
} catch (error) {
|
|
461
|
+
if (error instanceof ScheduleInputError) {
|
|
462
|
+
const code = codes.find((code) => code === error.code);
|
|
463
|
+
if (code !== void 0) return {
|
|
464
|
+
code,
|
|
465
|
+
message: error.message
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
return internalError();
|
|
469
|
+
}
|
|
399
470
|
}
|
|
400
471
|
/**
|
|
401
|
-
*
|
|
402
|
-
*
|
|
472
|
+
* Resolve one model target choice to a delivery destination.
|
|
473
|
+
*
|
|
474
|
+
* A created Session uses the caller's working directory and current composed preset; a preset selection in an
|
|
475
|
+
* empty Session changes that composition without rewriting the Session header.
|
|
476
|
+
* @param agent - Calling root Agent.
|
|
477
|
+
* @param target - Model target choice.
|
|
478
|
+
* @returns Destination request for the Schedule service.
|
|
403
479
|
*/
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
required: true,
|
|
417
|
-
description: "HH:mm:ss with optional 1-3 fractional digits, for example 23:00:00."
|
|
418
|
-
},
|
|
419
|
-
time_zone: {
|
|
420
|
-
type: "string",
|
|
421
|
-
required: true,
|
|
422
|
-
description: "UTC or IANA Area/Location, for example Asia/Shanghai."
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
},
|
|
426
|
-
weekly: {
|
|
427
|
-
type: "object",
|
|
428
|
-
additionalProperties: false,
|
|
429
|
-
description: "On the given weekdays at a local time.",
|
|
430
|
-
properties: {
|
|
431
|
-
time: {
|
|
432
|
-
type: "string",
|
|
433
|
-
required: true,
|
|
434
|
-
description: "HH:mm:ss with optional 1-3 fractional digits, for example 09:00:00."
|
|
435
|
-
},
|
|
436
|
-
time_zone: {
|
|
437
|
-
type: "string",
|
|
438
|
-
required: true,
|
|
439
|
-
description: "UTC or IANA Area/Location, for example Asia/Shanghai."
|
|
440
|
-
},
|
|
441
|
-
weekdays: {
|
|
442
|
-
type: "array",
|
|
443
|
-
required: true,
|
|
444
|
-
description: "ISO weekdays, Monday 1 through Sunday 7, without repetitions.",
|
|
445
|
-
items: { type: "integer" }
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
},
|
|
449
|
-
cron: {
|
|
450
|
-
type: "object",
|
|
451
|
-
additionalProperties: false,
|
|
452
|
-
description: "Five-field Vixie cron expression in a time zone.",
|
|
453
|
-
properties: {
|
|
454
|
-
expression: {
|
|
455
|
-
type: "string",
|
|
456
|
-
required: true,
|
|
457
|
-
description: "minute hour day-of-month month day-of-week, for example \"*/15 9-17 * * 1-5\". When both day fields are restricted, a date matches if either one matches."
|
|
458
|
-
},
|
|
459
|
-
time_zone: {
|
|
460
|
-
type: "string",
|
|
461
|
-
required: true,
|
|
462
|
-
description: "UTC or IANA Area/Location, for example Asia/Shanghai."
|
|
463
|
-
}
|
|
480
|
+
function targetFor(agent, target) {
|
|
481
|
+
if (target === "this_session") return {
|
|
482
|
+
kind: "session",
|
|
483
|
+
sessionId: agent.session.id
|
|
484
|
+
};
|
|
485
|
+
const cwd = agent.session.header.cwd;
|
|
486
|
+
const agentPreset = agent.ctx.get("agentPresets")?.composedPreset(agent.ctx);
|
|
487
|
+
return {
|
|
488
|
+
kind: target,
|
|
489
|
+
create: {
|
|
490
|
+
...cwd === void 0 ? {} : { cwd },
|
|
491
|
+
...agentPreset === void 0 ? {} : { agentPreset }
|
|
464
492
|
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
description: "Absolute target: an RFC 3339 date-time with offset, or a local date, time, and IANA time_zone.",
|
|
468
|
-
oneOf: [{ type: "string" }, {
|
|
469
|
-
type: "object",
|
|
470
|
-
additionalProperties: false,
|
|
471
|
-
properties: {
|
|
472
|
-
date: {
|
|
473
|
-
type: "string",
|
|
474
|
-
required: true
|
|
475
|
-
},
|
|
476
|
-
time: {
|
|
477
|
-
type: "string",
|
|
478
|
-
required: true
|
|
479
|
-
},
|
|
480
|
-
time_zone: {
|
|
481
|
-
type: "string",
|
|
482
|
-
required: true
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
}]
|
|
486
|
-
}
|
|
487
|
-
};
|
|
493
|
+
};
|
|
494
|
+
}
|
|
488
495
|
/**
|
|
489
|
-
* Register the four Schedule tools in the mounting scope.
|
|
496
|
+
* Register the four Schedule tools and shared guidance in the mounting scope.
|
|
490
497
|
*
|
|
491
|
-
* Each call
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
* @param ctx - Context owning the
|
|
498
|
+
* Each call acts for the Agent that dispatched it, so a mount outside an
|
|
499
|
+
* agent scope still registers the tools but every call without a caller Agent
|
|
500
|
+
* is refused as an internal failure.
|
|
501
|
+
* @param ctx - Context owning the tool and system-prompt registries and the Host Schedule service.
|
|
495
502
|
*/
|
|
496
503
|
function apply(ctx) {
|
|
497
|
-
ctx.inject(["schedule"],
|
|
498
|
-
registerScheduleTools(scheduleCtx);
|
|
499
|
-
});
|
|
504
|
+
ctx.inject(["schedule"], registerScheduleTools);
|
|
500
505
|
}
|
|
501
506
|
/**
|
|
502
|
-
* Register the
|
|
503
|
-
* service.
|
|
507
|
+
* Register the tools and shared guidance in the scope that resolved Schedule.
|
|
504
508
|
* @param ctx - Scope whose `schedule` service the tools act through.
|
|
505
509
|
*/
|
|
506
510
|
function registerScheduleTools(ctx) {
|
|
511
|
+
ctx.systemPrompt.section({
|
|
512
|
+
name: "tool:schedule",
|
|
513
|
+
order: ctx.systemPrompt.getSectionOrder("TOOL_SCHEDULE"),
|
|
514
|
+
text: ({ scope }) => ctx.tools.get("schedule_create", scope) !== void 0 || ctx.tools.get("schedule_edit", scope) !== void 0 ? MANAGEMENT_AUTHORIZATION : ""
|
|
515
|
+
});
|
|
516
|
+
const targetDescription = targetHelp(ctx.schedule.deliveryLimits().newSessionIntervalSeconds);
|
|
507
517
|
ctx.tools.register(defineTool({
|
|
508
518
|
name: "schedule_create",
|
|
509
519
|
description: CREATE_DESCRIPTION,
|
|
510
520
|
parameters: {
|
|
511
|
-
|
|
521
|
+
title: {
|
|
512
522
|
type: "string",
|
|
513
523
|
required: true,
|
|
514
|
-
description:
|
|
524
|
+
description: TITLE_HELP
|
|
515
525
|
},
|
|
516
|
-
|
|
526
|
+
prompt: {
|
|
517
527
|
type: "string",
|
|
518
528
|
required: true,
|
|
519
|
-
description:
|
|
529
|
+
description: "Instruction delivered at each occurrence."
|
|
520
530
|
},
|
|
521
|
-
|
|
522
|
-
type: "
|
|
523
|
-
|
|
531
|
+
rrule: {
|
|
532
|
+
type: "string",
|
|
533
|
+
required: true,
|
|
534
|
+
description: RRULE_HELP
|
|
524
535
|
},
|
|
525
|
-
|
|
536
|
+
target: {
|
|
537
|
+
type: "string",
|
|
538
|
+
required: true,
|
|
539
|
+
enum: TARGET_CHOICES,
|
|
540
|
+
description: targetDescription
|
|
541
|
+
}
|
|
526
542
|
},
|
|
527
543
|
output: {
|
|
528
544
|
schema: CREATE_OUTPUT_SCHEMA,
|
|
529
545
|
render: renderValue
|
|
530
546
|
},
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
} catch (error) {
|
|
542
|
-
return operationError(error);
|
|
543
|
-
}
|
|
547
|
+
execute(args, exec) {
|
|
548
|
+
return runForCaller(exec, CREATE_ERRORS, (agent) => ctx.schedule.create({
|
|
549
|
+
title: args.title,
|
|
550
|
+
prompt: args.prompt,
|
|
551
|
+
timing: {
|
|
552
|
+
kind: "rrule",
|
|
553
|
+
rrule: args.rrule
|
|
554
|
+
},
|
|
555
|
+
target: targetFor(agent, args.target)
|
|
556
|
+
}, agent.session.id, exec.signal).then(modelTask));
|
|
544
557
|
},
|
|
545
|
-
presentCall: (args) => present("Create
|
|
558
|
+
presentCall: (args) => present("Create scheduled task", "other", args.title)
|
|
546
559
|
}));
|
|
547
560
|
ctx.tools.register(defineTool({
|
|
548
561
|
name: "schedule_list",
|
|
549
562
|
description: LIST_DESCRIPTION,
|
|
550
|
-
parameters: {
|
|
563
|
+
parameters: {
|
|
564
|
+
active: {
|
|
565
|
+
type: "boolean",
|
|
566
|
+
description: "true for active tasks only; false for paused and ended tasks only; omitted for all tasks."
|
|
567
|
+
},
|
|
568
|
+
limit: {
|
|
569
|
+
type: "integer",
|
|
570
|
+
description: `Page size from 1 to ${MAX_LIST_LIMIT}; defaults to ${DEFAULT_LIST_LIMIT}.`
|
|
571
|
+
},
|
|
572
|
+
offset: {
|
|
573
|
+
type: "integer",
|
|
574
|
+
description: "Number of tasks to skip; defaults to 0."
|
|
575
|
+
}
|
|
576
|
+
},
|
|
551
577
|
output: {
|
|
552
578
|
schema: LIST_OUTPUT_SCHEMA,
|
|
553
579
|
render: renderValue
|
|
554
580
|
},
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
}
|
|
564
|
-
return operationError(error);
|
|
565
|
-
}
|
|
581
|
+
execute({ active, limit, offset }, exec) {
|
|
582
|
+
return runForCaller(exec, LIST_ERRORS, () => ctx.schedule.list({
|
|
583
|
+
...active === void 0 ? {} : { active },
|
|
584
|
+
...limit === void 0 ? {} : { limit },
|
|
585
|
+
...offset === void 0 ? {} : { offset }
|
|
586
|
+
}).then((page) => ({
|
|
587
|
+
...page,
|
|
588
|
+
items: page.items.map(modelTask)
|
|
589
|
+
})));
|
|
566
590
|
},
|
|
567
|
-
presentCall: () => present("List
|
|
591
|
+
presentCall: () => present("List scheduled tasks", "read")
|
|
568
592
|
}));
|
|
569
593
|
ctx.tools.register(defineTool({
|
|
570
|
-
name: "
|
|
571
|
-
description:
|
|
594
|
+
name: "schedule_view",
|
|
595
|
+
description: VIEW_DESCRIPTION,
|
|
572
596
|
parameters: { id: {
|
|
573
597
|
type: "string",
|
|
574
598
|
required: true,
|
|
575
|
-
description: "
|
|
599
|
+
description: "Task id."
|
|
576
600
|
} },
|
|
577
601
|
output: {
|
|
578
|
-
schema:
|
|
602
|
+
schema: VIEW_OUTPUT_SCHEMA,
|
|
579
603
|
render: renderValue
|
|
580
604
|
},
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
code: "invalid_rule",
|
|
584
|
-
message: "schedule_delete id must be non-empty without surrounding whitespace."
|
|
585
|
-
};
|
|
586
|
-
const id = ScheduleId(args.id);
|
|
587
|
-
const agent = exec.agent;
|
|
588
|
-
if (agent === void 0) return internalError();
|
|
589
|
-
const refusal = subagentCallerRefusal(agent);
|
|
590
|
-
if (refusal !== void 0) return refusal;
|
|
591
|
-
if (exec.signal.aborted) return internalError();
|
|
592
|
-
try {
|
|
593
|
-
return await ctx.schedule.delete({
|
|
594
|
-
sessionId: agent.session.id,
|
|
595
|
-
id
|
|
596
|
-
}, exec.signal);
|
|
597
|
-
} catch (error) {
|
|
598
|
-
return operationError(error);
|
|
599
|
-
}
|
|
605
|
+
execute(args, exec) {
|
|
606
|
+
return runForCaller(exec, CALLER_ERRORS, () => ctx.schedule.view({ id: ScheduleId(args.id) }).then((result) => "kind" in result ? modelTask(result) : result));
|
|
600
607
|
},
|
|
601
|
-
presentCall: (args) => present("
|
|
608
|
+
presentCall: (args) => present("View scheduled task", "read", args.id)
|
|
602
609
|
}));
|
|
603
610
|
ctx.tools.register(defineTool({
|
|
604
|
-
name: "
|
|
605
|
-
description:
|
|
611
|
+
name: "schedule_edit",
|
|
612
|
+
description: EDIT_DESCRIPTION,
|
|
606
613
|
parameters: {
|
|
607
614
|
id: {
|
|
608
615
|
type: "string",
|
|
609
616
|
required: true,
|
|
610
|
-
description: "
|
|
617
|
+
description: "Task id."
|
|
618
|
+
},
|
|
619
|
+
action: {
|
|
620
|
+
type: "string",
|
|
621
|
+
required: true,
|
|
622
|
+
enum: ["update", "delete"],
|
|
623
|
+
description: "update applies the provided fields, at least one; delete removes the task."
|
|
611
624
|
},
|
|
612
625
|
title: {
|
|
613
626
|
type: "string",
|
|
614
|
-
description:
|
|
627
|
+
description: TITLE_HELP
|
|
615
628
|
},
|
|
616
629
|
prompt: {
|
|
617
630
|
type: "string",
|
|
618
|
-
description: "New
|
|
631
|
+
description: "New instruction delivered at each occurrence."
|
|
619
632
|
},
|
|
620
|
-
|
|
633
|
+
rrule: {
|
|
634
|
+
type: "string",
|
|
635
|
+
description: "New recurrence in the schedule_create rrule format."
|
|
636
|
+
},
|
|
637
|
+
target: {
|
|
638
|
+
type: "string",
|
|
639
|
+
enum: TARGET_CHOICES,
|
|
640
|
+
description: "Omit to retain the destination and its creation settings. Providing target replaces them; new_once reserves a different Session. New targets capture this Session's working directory and preset. Choices are the same as schedule_create."
|
|
641
|
+
},
|
|
642
|
+
active: {
|
|
643
|
+
type: "boolean",
|
|
644
|
+
description: "false pauses the task; true resumes only if its timing has a future occurrence. Otherwise provide a new rrule."
|
|
645
|
+
},
|
|
646
|
+
expectedUpdatedAt: {
|
|
647
|
+
type: "string",
|
|
648
|
+
description: "updatedAt from the latest result that returned this task; if the task changed after that result, the result is schedule_conflict and nothing changes."
|
|
649
|
+
}
|
|
621
650
|
},
|
|
622
651
|
output: {
|
|
623
|
-
schema:
|
|
652
|
+
schema: EDIT_OUTPUT_SCHEMA,
|
|
624
653
|
render: renderValue
|
|
625
654
|
},
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
655
|
+
execute(args, exec) {
|
|
656
|
+
return runForCaller(exec, EDIT_ERRORS, (agent) => {
|
|
657
|
+
const id = ScheduleId(args.id);
|
|
658
|
+
const expected = args.expectedUpdatedAt === void 0 ? {} : { expectedUpdatedAt: args.expectedUpdatedAt };
|
|
659
|
+
let request;
|
|
660
|
+
if (args.action === "delete") {
|
|
661
|
+
if (PATCH_FIELDS.some((field) => args[field] !== void 0)) throw new ScheduleInputError("invalid_selector", "delete accepts only id, action, and expectedUpdatedAt.");
|
|
662
|
+
request = {
|
|
663
|
+
id,
|
|
664
|
+
action: "delete",
|
|
665
|
+
...expected
|
|
666
|
+
};
|
|
667
|
+
} else {
|
|
668
|
+
const patch = {
|
|
669
|
+
...args.title === void 0 ? {} : { title: args.title },
|
|
670
|
+
...args.prompt === void 0 ? {} : { prompt: args.prompt },
|
|
671
|
+
...args.rrule === void 0 ? {} : { timing: {
|
|
672
|
+
kind: "rrule",
|
|
673
|
+
rrule: args.rrule
|
|
674
|
+
} },
|
|
675
|
+
...args.target === void 0 ? {} : { target: targetFor(agent, args.target) },
|
|
676
|
+
...args.active === void 0 ? {} : { active: args.active }
|
|
677
|
+
};
|
|
678
|
+
if (Object.keys(patch).length === 0) throw new ScheduleInputError("invalid_selector", "update requires at least one of title, prompt, rrule, target, or active.");
|
|
679
|
+
request = {
|
|
680
|
+
id,
|
|
681
|
+
action: "update",
|
|
682
|
+
patch,
|
|
683
|
+
...expected
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
return ctx.schedule.edit(request, agent.session.id, exec.signal).then((result) => "kind" in result ? modelTask(result) : result);
|
|
687
|
+
});
|
|
656
688
|
},
|
|
657
|
-
presentCall: (args) => present("
|
|
689
|
+
presentCall: (args) => present(args.action === "delete" ? "Delete scheduled task" : "Edit scheduled task", "other", args.id)
|
|
658
690
|
}));
|
|
659
691
|
}
|
|
660
692
|
//#endregion
|