@qilin-agent/schedule 3.1.5 → 3.1.7

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.
@@ -31,17 +31,17 @@ export declare class ScheduleLogError extends Error {
31
31
  */
32
32
  constructor(message: string);
33
33
  }
34
- /** Error from a model-supplied Schedule rule that cannot become a record. */
34
+ /** Error from a model-supplied Schedule rule or target Session that cannot become a record. */
35
35
  export declare class ScheduleInputError extends Error {
36
36
  /** Stable public Schedule input code. */
37
- readonly code: 'invalid_prompt' | 'invalid_selector' | 'invalid_rule' | 'invalid_time_zone' | 'not_future' | 'time_out_of_range' | 'frequency_too_high';
37
+ readonly code: 'invalid_prompt' | 'invalid_selector' | 'invalid_rule' | 'invalid_time_zone' | 'not_future' | 'time_out_of_range' | 'frequency_too_high' | 'subagent_session';
38
38
  /**
39
39
  * Construct a stable input failure.
40
40
  * @param code - Public Schedule error discriminator.
41
41
  * @param message - Stable public diagnostic.
42
42
  * @param options - Optional contained implementation cause.
43
43
  */
44
- constructor(code: 'invalid_prompt' | 'invalid_selector' | 'invalid_rule' | 'invalid_time_zone' | 'not_future' | 'time_out_of_range' | 'frequency_too_high', message: string, options?: ErrorOptions);
44
+ constructor(code: 'invalid_prompt' | 'invalid_selector' | 'invalid_rule' | 'invalid_time_zone' | 'not_future' | 'time_out_of_range' | 'frequency_too_high' | 'subagent_session', message: string, options?: ErrorOptions);
45
45
  }
46
46
  /** Pure replay result, retaining active create order and every used id. */
47
47
  export interface FoldedSchedules {
@@ -44,7 +44,7 @@ export class ScheduleLogError extends Error {
44
44
  this.name = 'ScheduleLogError';
45
45
  }
46
46
  }
47
- /** Error from a model-supplied Schedule rule that cannot become a record. */
47
+ /** Error from a model-supplied Schedule rule or target Session that cannot become a record. */
48
48
  export class ScheduleInputError extends Error {
49
49
  /** Stable public Schedule input code. */
50
50
  code;
@@ -56,6 +56,8 @@ export declare class ScheduleService extends TypertRemoteService {
56
56
  *
57
57
  * The request must supply a title; a missing, blank-after-trim, or over-long
58
58
  * title rejects with `invalid_prompt` instead of deriving one from the prompt.
59
+ * A Session a delegated child owns rejects with `subagent_session`, because delivery
60
+ * can never reach it: the child is one whose delegation depth is above zero.
59
61
  * The record is built from the clock reading taken before the request joins the
60
62
  * serialized queue, so a create that waits behind a longer operation keeps its
61
63
  * request-time anchor and may already be due when the queue reaches it.
@@ -90,7 +92,9 @@ export declare class ScheduleService extends TypertRemoteService {
90
92
  * Delete one task belonging to the selected Session, leaving queued messages intact.
91
93
  *
92
94
  * The row is removed: the task no longer schedules, leaves `list` and `catalog`, and its
93
- * saved delivery records go with it.
95
+ * saved delivery records go with it. A task bound to a Session a delegated child owns
96
+ * stays deletable even though creation and timing edits refuse that binding, so a task
97
+ * stored before that rule existed remains removable.
94
98
  * @param request - Session and exact task identity.
95
99
  * @param signal - Optional cancellation checked before persistence begins, including after FIFO waits.
96
100
  * @returns Whether that Session owned a deleted task. Cancellation does not roll back an in-flight write.
@@ -101,10 +105,13 @@ export declare class ScheduleService extends TypertRemoteService {
101
105
  * binding without activating the Session or changing saved deliveries.
102
106
  *
103
107
  * Each supplied field replaces its stored value; an omitted field keeps it. A name or
104
- * instruction change alone does not reset the committed target.
108
+ * instruction change alone does not reset the committed target. A Session a delegated
109
+ * child owns returns the non-mutating `subagent_session` result, so an edit cannot
110
+ * re-arm a task bound to a Session delivery can never reach, and the Web editor can
111
+ * explain the refusal through the ordinary result it already renders.
105
112
  * @param request - Task binding, complete observed record, and any combination of timing, name, and instruction.
106
113
  * @param signal - Cancellation checked after domain readiness and FIFO waits, before persistence begins.
107
- * @returns The committed record, unchanged record for a no-op, or a non-mutating input/lookup/conflict result.
114
+ * @returns The committed record, unchanged record for a no-op, or a non-mutating input/lookup/conflict/refusal result.
108
115
  * Storage and lifecycle failures reject; cancellation after a write starts does not roll it back.
109
116
  */
110
117
  update(request: ScheduleUpdateRequest, signal?: AbortSignal): Promise<ScheduleUpdateResult>;
@@ -116,6 +123,19 @@ export declare class ScheduleService extends TypertRemoteService {
116
123
  */
117
124
  private emitChanged;
118
125
  private getDomain;
126
+ /**
127
+ * Refuse a Session no delivery can reach because a delegated child owns it.
128
+ *
129
+ * Delivery resolves the bound Session through `ctx.sessionController.resolveAgent`,
130
+ * which rejects the live Agent of a delegated child; a stored task for such a Session
131
+ * would stay permanently overdue and retry on every drive. Both operations that can
132
+ * arm a delivery read that same fact here instead of restating it: a delegated child
133
+ * is one whose {@link delegationDepthOf} is above zero, the accounting the delegation
134
+ * cap itself enforces, which survives a cold resume through the persisted header.
135
+ * @param sessionId - Session the task would be bound to.
136
+ * @returns The stable refusal for a delegated child's Session, or undefined when the Session is eligible.
137
+ */
138
+ private reminderTargetRefusal;
119
139
  /**
120
140
  * Remove every active task stored for one Session, inside the queue the tools
121
141
  * use.
@@ -37,6 +37,7 @@ import { randomUUID } from 'node:crypto';
37
37
  import z from '@qilin-agent/schemastery';
38
38
  import { Service } from '@qilin-agent/kylin';
39
39
  import { TypertRemoteService, Remote } from '@qilin-agent/typert-protocol';
40
+ import { delegationDepthOf } from '@qilin-agent/subagent';
40
41
  import { ScheduleRuntime } from "./runtime.js";
41
42
  import { scheduleDomain } from "./storage.js";
42
43
  import { deliveryHistoryPage } from "./delivery-history.js";
@@ -79,7 +80,7 @@ let ScheduleService = (() => {
79
80
  __esDecorate(this, null, _update_decorators, { kind: "method", name: "update", static: false, private: false, access: { has: obj => "update" in obj, get: obj => obj.update }, metadata: _metadata }, null, _instanceExtraInitializers);
80
81
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
81
82
  }
82
- static inject = ['agents', 'sessions', 'tools', 'storageDomain', 'sessionController', 'sessionPersistence'];
83
+ static inject = ['agents', 'sessions', 'storageDomain', 'sessionController', 'sessionPersistence'];
83
84
  static Config = z.object({
84
85
  deliveryHistoryDays: z.number().step(1).min(1).max(3650).default(DEFAULT_DELIVERY_HISTORY_DAYS),
85
86
  deliveryHistoryRecords: z.number().step(1).min(1).max(10_000).default(DEFAULT_DELIVERY_HISTORY_RECORDS),
@@ -193,6 +194,8 @@ let ScheduleService = (() => {
193
194
  *
194
195
  * The request must supply a title; a missing, blank-after-trim, or over-long
195
196
  * title rejects with `invalid_prompt` instead of deriving one from the prompt.
197
+ * A Session a delegated child owns rejects with `subagent_session`, because delivery
198
+ * can never reach it: the child is one whose delegation depth is above zero.
196
199
  * The record is built from the clock reading taken before the request joins the
197
200
  * serialized queue, so a create that waits behind a longer operation keeps its
198
201
  * request-time anchor and may already be due when the queue reaches it.
@@ -229,6 +232,9 @@ let ScheduleService = (() => {
229
232
  else
230
233
  throw new ScheduleInputError('invalid_selector', 'Exactly one reminder selector is required.');
231
234
  return this.serialize(async () => {
235
+ const refusal = this.reminderTargetRefusal(sessionId);
236
+ if (refusal !== undefined)
237
+ throw new ScheduleInputError('subagent_session', refusal.message);
232
238
  const domain = await this.getDomain();
233
239
  signal?.throwIfAborted();
234
240
  await domain.table('tasks').put(id, {
@@ -286,7 +292,9 @@ let ScheduleService = (() => {
286
292
  * Delete one task belonging to the selected Session, leaving queued messages intact.
287
293
  *
288
294
  * The row is removed: the task no longer schedules, leaves `list` and `catalog`, and its
289
- * saved delivery records go with it.
295
+ * saved delivery records go with it. A task bound to a Session a delegated child owns
296
+ * stays deletable even though creation and timing edits refuse that binding, so a task
297
+ * stored before that rule existed remains removable.
290
298
  * @param request - Session and exact task identity.
291
299
  * @param signal - Optional cancellation checked before persistence begins, including after FIFO waits.
292
300
  * @returns Whether that Session owned a deleted task. Cancellation does not roll back an in-flight write.
@@ -310,14 +318,20 @@ let ScheduleService = (() => {
310
318
  * binding without activating the Session or changing saved deliveries.
311
319
  *
312
320
  * Each supplied field replaces its stored value; an omitted field keeps it. A name or
313
- * instruction change alone does not reset the committed target.
321
+ * instruction change alone does not reset the committed target. A Session a delegated
322
+ * child owns returns the non-mutating `subagent_session` result, so an edit cannot
323
+ * re-arm a task bound to a Session delivery can never reach, and the Web editor can
324
+ * explain the refusal through the ordinary result it already renders.
314
325
  * @param request - Task binding, complete observed record, and any combination of timing, name, and instruction.
315
326
  * @param signal - Cancellation checked after domain readiness and FIFO waits, before persistence begins.
316
- * @returns The committed record, unchanged record for a no-op, or a non-mutating input/lookup/conflict result.
327
+ * @returns The committed record, unchanged record for a no-op, or a non-mutating input/lookup/conflict/refusal result.
317
328
  * Storage and lifecycle failures reject; cancellation after a write starts does not roll it back.
318
329
  */
319
330
  async update(request, signal) {
320
331
  return this.serialize(async () => {
332
+ const refusal = this.reminderTargetRefusal(request.sessionId);
333
+ if (refusal !== undefined)
334
+ return refusal;
321
335
  const tasks = (await this.getDomain()).table('tasks');
322
336
  signal?.throwIfAborted();
323
337
  const current = tasks.get(request.id);
@@ -356,6 +370,27 @@ let ScheduleService = (() => {
356
370
  await this.initialized;
357
371
  return this.ready;
358
372
  }
373
+ /**
374
+ * Refuse a Session no delivery can reach because a delegated child owns it.
375
+ *
376
+ * Delivery resolves the bound Session through `ctx.sessionController.resolveAgent`,
377
+ * which rejects the live Agent of a delegated child; a stored task for such a Session
378
+ * would stay permanently overdue and retry on every drive. Both operations that can
379
+ * arm a delivery read that same fact here instead of restating it: a delegated child
380
+ * is one whose {@link delegationDepthOf} is above zero, the accounting the delegation
381
+ * cap itself enforces, which survives a cold resume through the persisted header.
382
+ * @param sessionId - Session the task would be bound to.
383
+ * @returns The stable refusal for a delegated child's Session, or undefined when the Session is eligible.
384
+ */
385
+ reminderTargetRefusal(sessionId) {
386
+ const agent = this.ctx.agents.get(sessionId);
387
+ if (agent === undefined || delegationDepthOf(agent) === 0)
388
+ return undefined;
389
+ return {
390
+ code: 'subagent_session',
391
+ message: 'This Session belongs to subagent routing, which never receives reminder delivery.',
392
+ };
393
+ }
359
394
  /**
360
395
  * Remove every active task stored for one Session, inside the queue the tools
361
396
  * use.
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Task scheduling",
4
+ "description": "Stores automation tasks, runs each in its original session when it is due, and gives the agent tools to manage them."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "任务调度",
4
+ "description": "保存自动化任务,到点后在原会话中执行,并为智能体提供管理任务的工具。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qilin-agent/schedule",
3
3
  "description": "Host-wide durable reminders with shared management and original-Session delivery",
4
- "version": "3.1.5",
4
+ "version": "3.1.7",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -34,9 +34,11 @@
34
34
  "./remote": {
35
35
  "types": "./lib/typert.remote-client.d.ts",
36
36
  "default": "./lib/typert.remote-client.js"
37
- }
37
+ },
38
+ "./locale/*.json": "./locale/*.json"
38
39
  },
39
40
  "files": [
41
+ "locale/*.json",
40
42
  "lib/index.js",
41
43
  "lib/types/**/*.js",
42
44
  "lib/types/**/*.d.ts",
@@ -47,41 +49,43 @@
47
49
  ],
48
50
  "license": "MIT",
49
51
  "peerDependencies": {
50
- "@qilin-agent/agent": "3.1.5",
51
- "@qilin-agent/brand": "3.1.5",
52
- "@qilin-agent/api-session-controller": "3.1.5",
53
- "@qilin-agent/llm": "3.1.5",
54
- "@qilin-agent/session": "3.1.5",
55
- "@qilin-agent/session-persistence": "3.1.5",
56
- "@qilin-agent/session-projection": "3.1.5",
57
- "@qilin-agent/typert-protocol": "3.1.5",
58
- "@qilin-agent/storage-domain": "3.1.5",
59
- "@qilin-agent/kylin": "~4.0.3",
60
- "@qilin-agent/tools": "3.1.5"
52
+ "@qilin-agent/agent": "3.1.7",
53
+ "@qilin-agent/api-session-controller": "3.1.7",
54
+ "@qilin-agent/brand": "3.1.7",
55
+ "@qilin-agent/llm": "3.1.7",
56
+ "@qilin-agent/session": "3.1.7",
57
+ "@qilin-agent/storage-domain": "3.1.7",
58
+ "@qilin-agent/session-projection": "3.1.7",
59
+ "@qilin-agent/tools": "3.1.7",
60
+ "@qilin-agent/subagent": "3.1.7",
61
+ "@qilin-agent/typert-protocol": "3.1.7",
62
+ "@qilin-agent/kylin": "~4.0.5",
63
+ "@qilin-agent/session-persistence": "3.1.7"
61
64
  },
62
65
  "dependencies": {
63
66
  "@js-temporal/polyfill": "0.5.1",
64
67
  "zod": "^4.4.3",
65
- "@qilin-agent/schemastery": "~3.18.3"
68
+ "@qilin-agent/schemastery": "~3.18.5"
66
69
  },
67
70
  "devDependencies": {
68
- "@qilin-agent/agent": "3.1.5",
69
- "@qilin-agent/kylin-plugin-loader": "~1.0.4",
70
- "@qilin-agent/agent-loop": "3.1.5",
71
- "@qilin-agent/api-session-controller": "3.1.5",
72
- "@qilin-agent/agent-loop-testkit": "3.1.5",
73
- "@qilin-agent/llm": "3.1.5",
74
- "@qilin-agent/session": "3.1.5",
75
- "@qilin-agent/brand": "3.1.5",
76
- "@qilin-agent/session-persistence": "3.1.5",
77
- "@qilin-agent/session-persistence-jsonl": "3.1.5",
78
- "@qilin-agent/session-projection": "3.1.5",
79
- "@qilin-agent/storage-domain": "3.1.5",
80
- "@qilin-agent/storage-json": "3.1.5",
81
- "@qilin-agent/system-prompt": "3.1.5",
82
- "@qilin-agent/storage": "3.1.5",
83
- "@qilin-agent/tools": "3.1.5",
84
- "@qilin-agent/kylin": "~4.0.3",
85
- "@qilin-agent/typert-protocol": "3.1.5"
71
+ "@qilin-agent/kylin-plugin-loader": "~1.0.6",
72
+ "@qilin-agent/agent": "3.1.7",
73
+ "@qilin-agent/agent-loop-testkit": "3.1.7",
74
+ "@qilin-agent/agent-loop": "3.1.7",
75
+ "@qilin-agent/api-session-controller": "3.1.7",
76
+ "@qilin-agent/brand": "3.1.7",
77
+ "@qilin-agent/llm": "3.1.7",
78
+ "@qilin-agent/session-persistence": "3.1.7",
79
+ "@qilin-agent/session-projection": "3.1.7",
80
+ "@qilin-agent/session-persistence-jsonl": "3.1.7",
81
+ "@qilin-agent/storage-domain": "3.1.7",
82
+ "@qilin-agent/storage": "3.1.7",
83
+ "@qilin-agent/storage-json": "3.1.7",
84
+ "@qilin-agent/session": "3.1.7",
85
+ "@qilin-agent/subagent": "3.1.7",
86
+ "@qilin-agent/tools": "3.1.7",
87
+ "@qilin-agent/system-prompt": "3.1.7",
88
+ "@qilin-agent/typert-protocol": "3.1.7",
89
+ "@qilin-agent/kylin": "~4.0.5"
86
90
  }
87
91
  }