@qilin-agent/tool-schedule 3.1.7 → 3.1.9
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 -444
- package/lib/types/index.d.ts +9 -8
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +22 -15
package/lib/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { delegationDepthOf } from "@qilin-agent/subagent";
|
|
2
2
|
import { defineTool } from "@qilin-agent/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 "@qilin-agent/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 @qilin-agent/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,188 +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
|
|
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
|
+
];
|
|
261
405
|
/** Deterministic model content for every canonical Schedule value. */
|
|
262
406
|
function renderValue(_args, value) {
|
|
263
407
|
return [{
|
|
@@ -265,6 +409,19 @@ function renderValue(_args, value) {
|
|
|
265
409
|
text: JSON.stringify(value)
|
|
266
410
|
}];
|
|
267
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
|
+
}
|
|
268
425
|
/** Pure generic pending card. */
|
|
269
426
|
function present(title, kind, rawInput) {
|
|
270
427
|
return {
|
|
@@ -281,379 +438,255 @@ function internalError() {
|
|
|
281
438
|
message: "The schedule operation failed."
|
|
282
439
|
};
|
|
283
440
|
}
|
|
284
|
-
/** Translate invalid input while withholding internal storage failures. */
|
|
285
|
-
function operationError(error) {
|
|
286
|
-
return error instanceof ScheduleInputError ? {
|
|
287
|
-
code: error.code,
|
|
288
|
-
message: error.message
|
|
289
|
-
} : internalError();
|
|
290
|
-
}
|
|
291
441
|
/**
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
* @
|
|
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.
|
|
297
450
|
*/
|
|
298
|
-
function
|
|
299
|
-
|
|
300
|
-
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 {
|
|
301
455
|
code: "subagent_session",
|
|
302
|
-
message: "A delegated subagent cannot use
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
/** One supplied fixed-rate interval: a safe integer at or above the Host floor, or undefined. */
|
|
306
|
-
function invalidInterval(everySeconds) {
|
|
307
|
-
if (everySeconds === void 0) return void 0;
|
|
308
|
-
if (!Number.isSafeInteger(everySeconds)) return {
|
|
309
|
-
code: "invalid_rule",
|
|
310
|
-
message: "every_seconds must be a safe integer."
|
|
311
|
-
};
|
|
312
|
-
if (everySeconds < MIN_EVERY_INTERVAL_SECONDS) return {
|
|
313
|
-
code: "frequency_too_high",
|
|
314
|
-
message: `every_seconds must be at least ${MIN_EVERY_INTERVAL_SECONDS}.`
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
/** Validate selector constraints that the open parameter root cannot express. */
|
|
318
|
-
function validateCreateArgs(args) {
|
|
319
|
-
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 {
|
|
320
|
-
code: "invalid_selector",
|
|
321
|
-
message: "schedule_create accepts exactly one of after_seconds, at, every_seconds, daily, weekly, or cron."
|
|
322
|
-
};
|
|
323
|
-
if (args.prompt.trim().length === 0) return {
|
|
324
|
-
code: "invalid_prompt",
|
|
325
|
-
message: "prompt must be non-empty after trimming."
|
|
326
|
-
};
|
|
327
|
-
if (args.title.trim().length === 0) return {
|
|
328
|
-
code: "invalid_prompt",
|
|
329
|
-
message: REQUIRED_TITLE_MESSAGE
|
|
330
|
-
};
|
|
331
|
-
if (args.title.trim().length > MAX_TITLE_LENGTH) return {
|
|
332
|
-
code: "invalid_prompt",
|
|
333
|
-
message: `title must be at most ${MAX_TITLE_LENGTH} characters.`
|
|
334
|
-
};
|
|
335
|
-
if (args.after_seconds !== void 0 && (!Number.isSafeInteger(args.after_seconds) || args.after_seconds <= 0)) return {
|
|
336
|
-
code: "invalid_rule",
|
|
337
|
-
message: "after_seconds must be a positive safe integer."
|
|
338
|
-
};
|
|
339
|
-
return invalidInterval(args.every_seconds);
|
|
340
|
-
}
|
|
341
|
-
/** Validate the in-place update's selector count, id, and any supplied name, instruction, or interval. */
|
|
342
|
-
function validateUpdateArgs(args) {
|
|
343
|
-
const selectors = [
|
|
344
|
-
args.at !== void 0,
|
|
345
|
-
args.every_seconds !== void 0,
|
|
346
|
-
args.daily !== void 0,
|
|
347
|
-
args.weekly !== void 0,
|
|
348
|
-
args.cron !== void 0
|
|
349
|
-
].filter(Boolean).length;
|
|
350
|
-
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 {
|
|
351
|
-
code: "invalid_selector",
|
|
352
|
-
message: "schedule_update accepts at most one of at, every_seconds, daily, weekly, or cron."
|
|
353
|
-
};
|
|
354
|
-
if (args.id.length === 0 || args.id.trim() !== args.id) return {
|
|
355
|
-
code: "invalid_rule",
|
|
356
|
-
message: "schedule_update id must be non-empty without surrounding whitespace."
|
|
357
|
-
};
|
|
358
|
-
if (selectors === 0 && args.title === void 0 && args.prompt === void 0) return {
|
|
359
|
-
code: "invalid_selector",
|
|
360
|
-
message: "schedule_update needs a new title, prompt, or one of at, every_seconds, daily, weekly, or cron."
|
|
361
|
-
};
|
|
362
|
-
if (args.title !== void 0 && args.title.trim().length === 0) return {
|
|
363
|
-
code: "invalid_prompt",
|
|
364
|
-
message: REQUIRED_TITLE_MESSAGE
|
|
365
|
-
};
|
|
366
|
-
if (args.title !== void 0 && args.title.trim().length > MAX_TITLE_LENGTH) return {
|
|
367
|
-
code: "invalid_prompt",
|
|
368
|
-
message: `title must be at most ${MAX_TITLE_LENGTH} characters.`
|
|
369
|
-
};
|
|
370
|
-
if (args.prompt !== void 0 && args.prompt.trim().length === 0) return {
|
|
371
|
-
code: "invalid_prompt",
|
|
372
|
-
message: "prompt must be non-empty after trimming."
|
|
373
|
-
};
|
|
374
|
-
return invalidInterval(args.every_seconds);
|
|
375
|
-
}
|
|
376
|
-
/** The one timing replacement the update carries, or undefined when the request keeps the committed target. */
|
|
377
|
-
function timingChangeFrom(args) {
|
|
378
|
-
if (args.at !== void 0) return {
|
|
379
|
-
kind: "at",
|
|
380
|
-
at: args.at
|
|
381
|
-
};
|
|
382
|
-
if (args.every_seconds !== void 0) return {
|
|
383
|
-
kind: "every",
|
|
384
|
-
every_seconds: args.every_seconds
|
|
385
|
-
};
|
|
386
|
-
if (args.daily !== void 0) return {
|
|
387
|
-
kind: "daily",
|
|
388
|
-
daily: args.daily
|
|
389
|
-
};
|
|
390
|
-
if (args.weekly !== void 0) return {
|
|
391
|
-
kind: "weekly",
|
|
392
|
-
weekly: args.weekly
|
|
393
|
-
};
|
|
394
|
-
if (args.cron !== void 0) return {
|
|
395
|
-
kind: "cron",
|
|
396
|
-
cron: args.cron
|
|
456
|
+
message: "A delegated subagent cannot use scheduled tasks."
|
|
397
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
|
+
}
|
|
398
470
|
}
|
|
399
471
|
/**
|
|
400
|
-
*
|
|
401
|
-
*
|
|
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.
|
|
402
479
|
*/
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
required: true,
|
|
416
|
-
description: "HH:mm:ss with optional 1-3 fractional digits, for example 23:00:00."
|
|
417
|
-
},
|
|
418
|
-
time_zone: {
|
|
419
|
-
type: "string",
|
|
420
|
-
required: true,
|
|
421
|
-
description: "UTC or IANA Area/Location, for example Asia/Shanghai."
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
},
|
|
425
|
-
weekly: {
|
|
426
|
-
type: "object",
|
|
427
|
-
additionalProperties: false,
|
|
428
|
-
description: "On the given weekdays at a local time.",
|
|
429
|
-
properties: {
|
|
430
|
-
time: {
|
|
431
|
-
type: "string",
|
|
432
|
-
required: true,
|
|
433
|
-
description: "HH:mm:ss with optional 1-3 fractional digits, for example 09:00:00."
|
|
434
|
-
},
|
|
435
|
-
time_zone: {
|
|
436
|
-
type: "string",
|
|
437
|
-
required: true,
|
|
438
|
-
description: "UTC or IANA Area/Location, for example Asia/Shanghai."
|
|
439
|
-
},
|
|
440
|
-
weekdays: {
|
|
441
|
-
type: "array",
|
|
442
|
-
required: true,
|
|
443
|
-
description: "ISO weekdays, Monday 1 through Sunday 7, without repetitions.",
|
|
444
|
-
items: { type: "integer" }
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
},
|
|
448
|
-
cron: {
|
|
449
|
-
type: "object",
|
|
450
|
-
additionalProperties: false,
|
|
451
|
-
description: "Five-field Vixie cron expression in a time zone.",
|
|
452
|
-
properties: {
|
|
453
|
-
expression: {
|
|
454
|
-
type: "string",
|
|
455
|
-
required: true,
|
|
456
|
-
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."
|
|
457
|
-
},
|
|
458
|
-
time_zone: {
|
|
459
|
-
type: "string",
|
|
460
|
-
required: true,
|
|
461
|
-
description: "UTC or IANA Area/Location, for example Asia/Shanghai."
|
|
462
|
-
}
|
|
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 }
|
|
463
492
|
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
description: "Absolute target: an RFC 3339 date-time with offset, or a local date, time, and IANA time_zone.",
|
|
467
|
-
oneOf: [{ type: "string" }, {
|
|
468
|
-
type: "object",
|
|
469
|
-
additionalProperties: false,
|
|
470
|
-
properties: {
|
|
471
|
-
date: {
|
|
472
|
-
type: "string",
|
|
473
|
-
required: true
|
|
474
|
-
},
|
|
475
|
-
time: {
|
|
476
|
-
type: "string",
|
|
477
|
-
required: true
|
|
478
|
-
},
|
|
479
|
-
time_zone: {
|
|
480
|
-
type: "string",
|
|
481
|
-
required: true
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}]
|
|
485
|
-
}
|
|
486
|
-
};
|
|
493
|
+
};
|
|
494
|
+
}
|
|
487
495
|
/**
|
|
488
|
-
* Register the four Schedule tools in the mounting scope.
|
|
496
|
+
* Register the four Schedule tools and shared guidance in the mounting scope.
|
|
489
497
|
*
|
|
490
|
-
* Each call
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
* @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.
|
|
494
502
|
*/
|
|
495
503
|
function apply(ctx) {
|
|
496
|
-
ctx.inject(["schedule"],
|
|
497
|
-
registerScheduleTools(scheduleCtx);
|
|
498
|
-
});
|
|
504
|
+
ctx.inject(["schedule"], registerScheduleTools);
|
|
499
505
|
}
|
|
500
506
|
/**
|
|
501
|
-
* Register the
|
|
502
|
-
* service.
|
|
507
|
+
* Register the tools and shared guidance in the scope that resolved Schedule.
|
|
503
508
|
* @param ctx - Scope whose `schedule` service the tools act through.
|
|
504
509
|
*/
|
|
505
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);
|
|
506
517
|
ctx.tools.register(defineTool({
|
|
507
518
|
name: "schedule_create",
|
|
508
519
|
description: CREATE_DESCRIPTION,
|
|
509
520
|
parameters: {
|
|
510
|
-
|
|
521
|
+
title: {
|
|
511
522
|
type: "string",
|
|
512
523
|
required: true,
|
|
513
|
-
description:
|
|
524
|
+
description: TITLE_HELP
|
|
514
525
|
},
|
|
515
|
-
|
|
526
|
+
prompt: {
|
|
516
527
|
type: "string",
|
|
517
528
|
required: true,
|
|
518
|
-
description:
|
|
529
|
+
description: "Instruction delivered at each occurrence."
|
|
519
530
|
},
|
|
520
|
-
|
|
521
|
-
type: "
|
|
522
|
-
|
|
531
|
+
rrule: {
|
|
532
|
+
type: "string",
|
|
533
|
+
required: true,
|
|
534
|
+
description: RRULE_HELP
|
|
523
535
|
},
|
|
524
|
-
|
|
536
|
+
target: {
|
|
537
|
+
type: "string",
|
|
538
|
+
required: true,
|
|
539
|
+
enum: TARGET_CHOICES,
|
|
540
|
+
description: targetDescription
|
|
541
|
+
}
|
|
525
542
|
},
|
|
526
543
|
output: {
|
|
527
544
|
schema: CREATE_OUTPUT_SCHEMA,
|
|
528
545
|
render: renderValue
|
|
529
546
|
},
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
} catch (error) {
|
|
541
|
-
return operationError(error);
|
|
542
|
-
}
|
|
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));
|
|
543
557
|
},
|
|
544
|
-
presentCall: (args) => present("Create
|
|
558
|
+
presentCall: (args) => present("Create scheduled task", "other", args.title)
|
|
545
559
|
}));
|
|
546
560
|
ctx.tools.register(defineTool({
|
|
547
561
|
name: "schedule_list",
|
|
548
562
|
description: LIST_DESCRIPTION,
|
|
549
|
-
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
|
+
},
|
|
550
577
|
output: {
|
|
551
578
|
schema: LIST_OUTPUT_SCHEMA,
|
|
552
579
|
render: renderValue
|
|
553
580
|
},
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
}
|
|
563
|
-
return operationError(error);
|
|
564
|
-
}
|
|
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
|
+
})));
|
|
565
590
|
},
|
|
566
|
-
presentCall: () => present("List
|
|
591
|
+
presentCall: () => present("List scheduled tasks", "read")
|
|
567
592
|
}));
|
|
568
593
|
ctx.tools.register(defineTool({
|
|
569
|
-
name: "
|
|
570
|
-
description:
|
|
594
|
+
name: "schedule_view",
|
|
595
|
+
description: VIEW_DESCRIPTION,
|
|
571
596
|
parameters: { id: {
|
|
572
597
|
type: "string",
|
|
573
598
|
required: true,
|
|
574
|
-
description: "
|
|
599
|
+
description: "Task id."
|
|
575
600
|
} },
|
|
576
601
|
output: {
|
|
577
|
-
schema:
|
|
602
|
+
schema: VIEW_OUTPUT_SCHEMA,
|
|
578
603
|
render: renderValue
|
|
579
604
|
},
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
code: "invalid_rule",
|
|
583
|
-
message: "schedule_delete id must be non-empty without surrounding whitespace."
|
|
584
|
-
};
|
|
585
|
-
const id = ScheduleId(args.id);
|
|
586
|
-
const agent = exec.agent;
|
|
587
|
-
if (agent === void 0) return internalError();
|
|
588
|
-
const refusal = subagentCallerRefusal(agent);
|
|
589
|
-
if (refusal !== void 0) return refusal;
|
|
590
|
-
if (exec.signal.aborted) return internalError();
|
|
591
|
-
try {
|
|
592
|
-
return await ctx.schedule.delete({
|
|
593
|
-
sessionId: agent.session.id,
|
|
594
|
-
id
|
|
595
|
-
}, exec.signal);
|
|
596
|
-
} catch (error) {
|
|
597
|
-
return operationError(error);
|
|
598
|
-
}
|
|
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));
|
|
599
607
|
},
|
|
600
|
-
presentCall: (args) => present("
|
|
608
|
+
presentCall: (args) => present("View scheduled task", "read", args.id)
|
|
601
609
|
}));
|
|
602
610
|
ctx.tools.register(defineTool({
|
|
603
|
-
name: "
|
|
604
|
-
description:
|
|
611
|
+
name: "schedule_edit",
|
|
612
|
+
description: EDIT_DESCRIPTION,
|
|
605
613
|
parameters: {
|
|
606
614
|
id: {
|
|
607
615
|
type: "string",
|
|
608
616
|
required: true,
|
|
609
|
-
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."
|
|
610
624
|
},
|
|
611
625
|
title: {
|
|
612
626
|
type: "string",
|
|
613
|
-
description:
|
|
627
|
+
description: TITLE_HELP
|
|
614
628
|
},
|
|
615
629
|
prompt: {
|
|
616
630
|
type: "string",
|
|
617
|
-
description: "New
|
|
631
|
+
description: "New instruction delivered at each occurrence."
|
|
618
632
|
},
|
|
619
|
-
|
|
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
|
+
}
|
|
620
650
|
},
|
|
621
651
|
output: {
|
|
622
|
-
schema:
|
|
652
|
+
schema: EDIT_OUTPUT_SCHEMA,
|
|
623
653
|
render: renderValue
|
|
624
654
|
},
|
|
625
|
-
|
|
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
|
+
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
|
+
});
|
|
655
688
|
},
|
|
656
|
-
presentCall: (args) => present("
|
|
689
|
+
presentCall: (args) => present(args.action === "delete" ? "Delete scheduled task" : "Edit scheduled task", "other", args.id)
|
|
657
690
|
}));
|
|
658
691
|
}
|
|
659
692
|
//#endregion
|