@deepseek-ai/dsh-schedule 0.2.0-rc.2 → 0.2.1-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -346,6 +346,9 @@ const _deepseek_ai_dsh_schedule_schedule_update_result$schema = () => (_deepseek
346
346
  }), z.object({
347
347
  'code': z.literal("frequency_too_high").readonly(),
348
348
  'message': z.string().readonly(),
349
+ }), z.object({
350
+ 'code': z.literal("subagent_session").readonly(),
351
+ 'message': z.string().readonly(),
349
352
  }), z.object({
350
353
  'code': z.literal("internal_error").readonly(),
351
354
  'message': z.string().readonly(),
@@ -370,7 +373,7 @@ export const TYPERT = {
370
373
  typeSymbol: '@deepseek-ai/dsh-schedule#schedule/catalog:result',
371
374
  create: _deepseek_ai_dsh_schedule_schedule_catalog_result$schema,
372
375
  },
373
- sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":304,"column":9},
376
+ sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":285,"column":9},
374
377
  },
375
378
  {
376
379
  id: '@deepseek-ai/dsh-schedule#schedule/delete',
@@ -396,7 +399,7 @@ export const TYPERT = {
396
399
  typeSymbol: '@deepseek-ai/dsh-schedule/client#ScheduleDeleteResult',
397
400
  create: _deepseek_ai_dsh_schedule_schedule_delete_result$schema,
398
401
  },
399
- sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":343,"column":9},
402
+ sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":326,"column":9},
400
403
  },
401
404
  {
402
405
  id: '@deepseek-ai/dsh-schedule#schedule/history',
@@ -421,7 +424,7 @@ export const TYPERT = {
421
424
  typeSymbol: '@deepseek-ai/dsh-schedule/client#ScheduleDeliveryHistoryResult',
422
425
  create: _deepseek_ai_dsh_schedule_schedule_history_result$schema,
423
426
  },
424
- sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":322,"column":9},
427
+ sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":303,"column":9},
425
428
  },
426
429
  {
427
430
  id: '@deepseek-ai/dsh-schedule#schedule/list',
@@ -446,7 +449,7 @@ export const TYPERT = {
446
449
  typeSymbol: '@deepseek-ai/dsh-schedule#schedule/list:result',
447
450
  create: _deepseek_ai_dsh_schedule_schedule_list_result$schema,
448
451
  },
449
- sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":290,"column":9},
452
+ sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":271,"column":9},
450
453
  },
451
454
  {
452
455
  id: '@deepseek-ai/dsh-schedule#schedule/update',
@@ -472,7 +475,7 @@ export const TYPERT = {
472
475
  typeSymbol: '@deepseek-ai/dsh-schedule/client#ScheduleUpdateResult',
473
476
  create: _deepseek_ai_dsh_schedule_schedule_update_result$schema,
474
477
  },
475
- sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":370,"column":9},
478
+ sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":356,"column":9},
476
479
  },
477
480
  ],
478
481
  model: {
@@ -495,7 +498,7 @@ export const TYPERT = {
495
498
  "name": "create",
496
499
  "signature": "async create(sessionId: SessionId, request: ScheduleCreateRequest, signal?: AbortSignal): Promise<ScheduleRecord>",
497
500
  "summary": "Create a reminder bound to the caller-selected Session without activating it.",
498
- "jsDoc": "/**\n * Create a reminder bound to the caller-selected Session without activating it.\n *\n * The request must supply a title; a missing, blank-after-trim, or over-long\n * title rejects with `invalid_prompt` instead of deriving one from the prompt.\n * The record is built from the clock reading taken before the request joins the\n * serialized queue, so a create that waits behind a longer operation keeps its\n * request-time anchor and may already be due when the queue reaches it.\n * @param sessionId - Original Session receiving the reminder.\n * @param request - Validated tool selector, required title, and reminder content.\n * @param signal - Optional cancellation checked before persistence begins, including after FIFO waits.\n * @returns The durably stored schedule. Cancellation does not roll back an in-flight write.\n */"
501
+ "jsDoc": "/**\n * Create a reminder bound to the caller-selected Session without activating it.\n *\n * The request must supply a title; a missing, blank-after-trim, or over-long\n * title rejects with `invalid_prompt` instead of deriving one from the prompt.\n * A Session a delegated child owns rejects with `subagent_session`, because delivery\n * can never reach it: the child is one whose delegation depth is above zero.\n * The record is built from the clock reading taken before the request joins the\n * serialized queue, so a create that waits behind a longer operation keeps its\n * request-time anchor and may already be due when the queue reaches it.\n * @param sessionId - Original Session receiving the reminder.\n * @param request - Validated tool selector, required title, and reminder content.\n * @param signal - Optional cancellation checked before persistence begins, including after FIFO waits.\n * @returns The durably stored schedule. Cancellation does not roll back an in-flight write.\n */"
499
502
  },
500
503
  {
501
504
  "kind": "method",
@@ -523,14 +526,14 @@ export const TYPERT = {
523
526
  "name": "delete",
524
527
  "signature": "@Remote('delete') async delete(request: ScheduleDeleteRequest, signal?: AbortSignal): Promise<ScheduleDeleteResult>",
525
528
  "summary": "Delete one task belonging to the selected Session, leaving queued messages intact.",
526
- "jsDoc": "/**\n * Delete one task belonging to the selected Session, leaving queued messages intact.\n *\n * The row is removed: the task no longer schedules, leaves `list` and `catalog`, and its\n * saved delivery records go with it.\n * @param request - Session and exact task identity.\n * @param signal - Optional cancellation checked before persistence begins, including after FIFO waits.\n * @returns Whether that Session owned a deleted task. Cancellation does not roll back an in-flight write.\n */"
529
+ "jsDoc": "/**\n * Delete one task belonging to the selected Session, leaving queued messages intact.\n *\n * The row is removed: the task no longer schedules, leaves `list` and `catalog`, and its\n * saved delivery records go with it. A task bound to a Session a delegated child owns\n * stays deletable even though creation and timing edits refuse that binding, so a task\n * stored before that rule existed remains removable.\n * @param request - Session and exact task identity.\n * @param signal - Optional cancellation checked before persistence begins, including after FIFO waits.\n * @returns Whether that Session owned a deleted task. Cancellation does not roll back an in-flight write.\n */"
527
530
  },
528
531
  {
529
532
  "kind": "method",
530
533
  "name": "update",
531
534
  "signature": "@Remote('update') async update(request: ScheduleUpdateRequest, signal?: AbortSignal): Promise<ScheduleUpdateResult>",
532
535
  "summary": "Update the name, instruction, and timing of an active task within the original Session binding without activating the Session or changing saved deliveries.",
533
- "jsDoc": "/**\n * Update the name, instruction, and timing of an active task within the original Session\n * binding without activating the Session or changing saved deliveries.\n *\n * Each supplied field replaces its stored value; an omitted field keeps it. A name or\n * instruction change alone does not reset the committed target.\n * @param request - Task binding, complete observed record, and any combination of timing, name, and instruction.\n * @param signal - Cancellation checked after domain readiness and FIFO waits, before persistence begins.\n * @returns The committed record, unchanged record for a no-op, or a non-mutating input/lookup/conflict result.\n * Storage and lifecycle failures reject; cancellation after a write starts does not roll it back.\n */"
536
+ "jsDoc": "/**\n * Update the name, instruction, and timing of an active task within the original Session\n * binding without activating the Session or changing saved deliveries.\n *\n * Each supplied field replaces its stored value; an omitted field keeps it. A name or\n * instruction change alone does not reset the committed target. A Session a delegated\n * child owns returns the non-mutating `subagent_session` result, so an edit cannot\n * re-arm a task bound to a Session delivery can never reach, and the Web editor can\n * explain the refusal through the ordinary result it already renders.\n * @param request - Task binding, complete observed record, and any combination of timing, name, and instruction.\n * @param signal - Cancellation checked after domain readiness and FIFO waits, before persistence begins.\n * @returns The committed record, unchanged record for a no-op, or a non-mutating input/lookup/conflict/refusal result.\n * Storage and lifecycle failures reject; cancellation after a write starts does not roll it back.\n */"
534
537
  }
535
538
  ],
536
539
  "types": [
@@ -668,7 +671,7 @@ export const TYPERT = {
668
671
  },
669
672
  {
670
673
  "name": "ScheduleToolError",
671
- "declaration": "export type ScheduleToolError = InvalidPromptError | InvalidSelectorError | InvalidRuleError | InvalidTimeZoneError | NotFutureError | TimeOutOfRangeError | FrequencyTooHighError | InternalScheduleError;"
674
+ "declaration": "export type ScheduleToolError = InvalidPromptError | InvalidSelectorError | InvalidRuleError | InvalidTimeZoneError | NotFutureError | TimeOutOfRangeError | FrequencyTooHighError | SubagentSessionError | InternalScheduleError;"
672
675
  },
673
676
  {
674
677
  "name": "ScheduleUpdateContent",
@@ -690,6 +693,10 @@ export const TYPERT = {
690
693
  "name": "SessionId",
691
694
  "declaration": "export type SessionId = Branded<'SessionId'>;"
692
695
  },
696
+ {
697
+ "name": "SubagentSessionError",
698
+ "declaration": "export interface SubagentSessionError {\n readonly code: 'subagent_session';\n readonly message: string;\n}"
699
+ },
693
700
  {
694
701
  "name": "TimeOutOfRangeError",
695
702
  "declaration": "export interface TimeOutOfRangeError {\n readonly code: 'time_out_of_range';\n readonly message: string;\n}"
@@ -346,6 +346,9 @@ const _deepseek_ai_dsh_schedule_schedule_update_result$schema = () => (_deepseek
346
346
  }), z.object({
347
347
  'code': z.literal("frequency_too_high").readonly(),
348
348
  'message': z.string().readonly(),
349
+ }), z.object({
350
+ 'code': z.literal("subagent_session").readonly(),
351
+ 'message': z.string().readonly(),
349
352
  }), z.object({
350
353
  'code': z.literal("internal_error").readonly(),
351
354
  'message': z.string().readonly(),
@@ -367,7 +370,7 @@ export const TYPERT_REMOTE = {
367
370
  typeSymbol: '@deepseek-ai/dsh-schedule#schedule/catalog:result',
368
371
  create: _deepseek_ai_dsh_schedule_schedule_catalog_result$schema,
369
372
  },
370
- sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":304,"column":9},
373
+ sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":285,"column":9},
371
374
  },
372
375
  {
373
376
  id: '@deepseek-ai/dsh-schedule#schedule/delete',
@@ -393,7 +396,7 @@ export const TYPERT_REMOTE = {
393
396
  typeSymbol: '@deepseek-ai/dsh-schedule/client#ScheduleDeleteResult',
394
397
  create: _deepseek_ai_dsh_schedule_schedule_delete_result$schema,
395
398
  },
396
- sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":343,"column":9},
399
+ sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":326,"column":9},
397
400
  },
398
401
  {
399
402
  id: '@deepseek-ai/dsh-schedule#schedule/history',
@@ -418,7 +421,7 @@ export const TYPERT_REMOTE = {
418
421
  typeSymbol: '@deepseek-ai/dsh-schedule/client#ScheduleDeliveryHistoryResult',
419
422
  create: _deepseek_ai_dsh_schedule_schedule_history_result$schema,
420
423
  },
421
- sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":322,"column":9},
424
+ sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":303,"column":9},
422
425
  },
423
426
  {
424
427
  id: '@deepseek-ai/dsh-schedule#schedule/list',
@@ -443,7 +446,7 @@ export const TYPERT_REMOTE = {
443
446
  typeSymbol: '@deepseek-ai/dsh-schedule#schedule/list:result',
444
447
  create: _deepseek_ai_dsh_schedule_schedule_list_result$schema,
445
448
  },
446
- sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":290,"column":9},
449
+ sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":271,"column":9},
447
450
  },
448
451
  {
449
452
  id: '@deepseek-ai/dsh-schedule#schedule/update',
@@ -469,7 +472,7 @@ export const TYPERT_REMOTE = {
469
472
  typeSymbol: '@deepseek-ai/dsh-schedule/client#ScheduleUpdateResult',
470
473
  create: _deepseek_ai_dsh_schedule_schedule_update_result$schema,
471
474
  },
472
- sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":370,"column":9},
475
+ sourceLocation: {"file":"packages/schedule/schedule/src/index.ts","line":356,"column":9},
473
476
  },
474
477
  ],
475
478
  }
@@ -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;
@@ -4,11 +4,10 @@ import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
4
4
  import type { SessionId } from '@deepseek-ai/dsh-session';
5
5
  import type { ScheduleCatalogEntry, ScheduleCreateRequest, ScheduleDeleteRequest, ScheduleDeleteResult, ScheduleDeliveryHistoryRequest, ScheduleDeliveryHistoryResult, ScheduleListRequest, ScheduleRecord, ScheduleUpdateRequest, ScheduleUpdateResult } from './types.ts';
6
6
  export type * from './types.ts';
7
- export { registerScheduleTools } from './tools.ts';
8
7
  export { scheduleDomain } from './storage.ts';
9
8
  export type { ScheduleTask } from './storage.ts';
10
9
  export type { RecurringOccurrence } from './domain.ts';
11
- export { SCHEDULE_CHANGE_VERSION, MIN_EVERY_INTERVAL_SECONDS, MAX_TITLE_LENGTH, ScheduleId, ScheduleInputError, ScheduleLogError, canonicalizeCronExpression, createAfterScheduleRecord, createAtScheduleRecord, createEveryScheduleRecord, createDailyScheduleRecord, createWeeklyScheduleRecord, createCronScheduleRecord, decodeScheduleChange, decodeScheduleRecord, foldScheduleEvents, isRecurringScheduleRecord, normalizeWeekdays, parseCronInput, parseWeeklyInput, renderReminderFraming, renderRecurringReminderBatchFraming, resolveEveryOccurrence, resolveDailyOccurrence, resolveWeeklyOccurrence, resolveCronOccurrence, resolveRecurringOccurrence, scheduleTitle, scheduleView, weeklyTime, } from './domain.ts';
10
+ export { SCHEDULE_CHANGE_VERSION, MIN_EVERY_INTERVAL_SECONDS, MAX_TITLE_LENGTH, REQUIRED_TITLE_MESSAGE, ScheduleId, ScheduleInputError, ScheduleLogError, canonicalizeCronExpression, createAfterScheduleRecord, createAtScheduleRecord, createEveryScheduleRecord, createDailyScheduleRecord, createWeeklyScheduleRecord, createCronScheduleRecord, decodeScheduleChange, decodeScheduleRecord, foldScheduleEvents, isRecurringScheduleRecord, normalizeWeekdays, parseCronInput, parseWeeklyInput, renderReminderFraming, renderRecurringReminderBatchFraming, resolveEveryOccurrence, resolveDailyOccurrence, resolveWeeklyOccurrence, resolveCronOccurrence, resolveRecurringOccurrence, scheduleTitle, scheduleView, weeklyTime, } from './domain.ts';
12
11
  declare module '@deepseek-ai/cordis' {
13
12
  interface Context {
14
13
  /** Durable Host-wide reminder management. */
@@ -57,6 +56,8 @@ export declare class ScheduleService extends TypertRemoteService {
57
56
  *
58
57
  * The request must supply a title; a missing, blank-after-trim, or over-long
59
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.
60
61
  * The record is built from the clock reading taken before the request joins the
61
62
  * serialized queue, so a create that waits behind a longer operation keeps its
62
63
  * request-time anchor and may already be due when the queue reaches it.
@@ -91,7 +92,9 @@ export declare class ScheduleService extends TypertRemoteService {
91
92
  * Delete one task belonging to the selected Session, leaving queued messages intact.
92
93
  *
93
94
  * The row is removed: the task no longer schedules, leaves `list` and `catalog`, and its
94
- * 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.
95
98
  * @param request - Session and exact task identity.
96
99
  * @param signal - Optional cancellation checked before persistence begins, including after FIFO waits.
97
100
  * @returns Whether that Session owned a deleted task. Cancellation does not roll back an in-flight write.
@@ -102,10 +105,13 @@ export declare class ScheduleService extends TypertRemoteService {
102
105
  * binding without activating the Session or changing saved deliveries.
103
106
  *
104
107
  * Each supplied field replaces its stored value; an omitted field keeps it. A name or
105
- * 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.
106
112
  * @param request - Task binding, complete observed record, and any combination of timing, name, and instruction.
107
113
  * @param signal - Cancellation checked after domain readiness and FIFO waits, before persistence begins.
108
- * @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.
109
115
  * Storage and lifecycle failures reject; cancellation after a write starts does not roll it back.
110
116
  */
111
117
  update(request: ScheduleUpdateRequest, signal?: AbortSignal): Promise<ScheduleUpdateResult>;
@@ -117,6 +123,19 @@ export declare class ScheduleService extends TypertRemoteService {
117
123
  */
118
124
  private emitChanged;
119
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;
120
139
  /**
121
140
  * Remove every active task stored for one Session, inside the queue the tools
122
141
  * use.
@@ -37,15 +37,14 @@ import { randomUUID } from 'node:crypto';
37
37
  import z from '@deepseek-ai/schemastery';
38
38
  import { Service } from '@deepseek-ai/cordis';
39
39
  import { TypertRemoteService, Remote } from '@deepseek-ai/dsh-typert-protocol';
40
+ import { delegationDepthOf } from '@deepseek-ai/dsh-subagent';
40
41
  import { ScheduleRuntime } from "./runtime.js";
41
- import { registerScheduleTools } from "./tools.js";
42
42
  import { scheduleDomain } from "./storage.js";
43
43
  import { deliveryHistoryPage } from "./delivery-history.js";
44
44
  import { resolveScheduleUpdate } from "./update.js";
45
45
  import { foldScheduleEvents, ScheduleInputError, ScheduleLogError, ScheduleId, createAfterScheduleRecord, createAtScheduleRecord, createEveryScheduleRecord, createDailyScheduleRecord, createWeeklyScheduleRecord, createCronScheduleRecord, scheduleTitle, } from "./domain.js";
46
- export { registerScheduleTools } from "./tools.js";
47
46
  export { scheduleDomain } from "./storage.js";
48
- export { SCHEDULE_CHANGE_VERSION, MIN_EVERY_INTERVAL_SECONDS, MAX_TITLE_LENGTH, ScheduleId, ScheduleInputError, ScheduleLogError, canonicalizeCronExpression, createAfterScheduleRecord, createAtScheduleRecord, createEveryScheduleRecord, createDailyScheduleRecord, createWeeklyScheduleRecord, createCronScheduleRecord, decodeScheduleChange, decodeScheduleRecord, foldScheduleEvents, isRecurringScheduleRecord, normalizeWeekdays, parseCronInput, parseWeeklyInput, renderReminderFraming, renderRecurringReminderBatchFraming, resolveEveryOccurrence, resolveDailyOccurrence, resolveWeeklyOccurrence, resolveCronOccurrence, resolveRecurringOccurrence, scheduleTitle, scheduleView, weeklyTime, } from "./domain.js";
47
+ export { SCHEDULE_CHANGE_VERSION, MIN_EVERY_INTERVAL_SECONDS, MAX_TITLE_LENGTH, REQUIRED_TITLE_MESSAGE, ScheduleId, ScheduleInputError, ScheduleLogError, canonicalizeCronExpression, createAfterScheduleRecord, createAtScheduleRecord, createEveryScheduleRecord, createDailyScheduleRecord, createWeeklyScheduleRecord, createCronScheduleRecord, decodeScheduleChange, decodeScheduleRecord, foldScheduleEvents, isRecurringScheduleRecord, normalizeWeekdays, parseCronInput, parseWeeklyInput, renderReminderFraming, renderRecurringReminderBatchFraming, resolveEveryOccurrence, resolveDailyOccurrence, resolveWeeklyOccurrence, resolveCronOccurrence, resolveRecurringOccurrence, scheduleTitle, scheduleView, weeklyTime, } from "./domain.js";
49
48
  /** Retained delivery-history window applied when the deployment states none. */
50
49
  const DEFAULT_DELIVERY_HISTORY_DAYS = 30;
51
50
  /** Retained delivery-history record cap applied when the deployment states none. */
@@ -81,7 +80,7 @@ let ScheduleService = (() => {
81
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);
82
81
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
83
82
  }
84
- static inject = ['agents', 'sessions', 'tools', 'storageDomain', 'sessionController', 'sessionPersistence'];
83
+ static inject = ['agents', 'sessions', 'storageDomain', 'sessionController', 'sessionPersistence'];
85
84
  static Config = z.object({
86
85
  deliveryHistoryDays: z.number().step(1).min(1).max(3650).default(DEFAULT_DELIVERY_HISTORY_DAYS),
87
86
  deliveryHistoryRecords: z.number().step(1).min(1).max(10_000).default(DEFAULT_DELIVERY_HISTORY_RECORDS),
@@ -142,27 +141,6 @@ let ScheduleService = (() => {
142
141
  this.runtime.requestDrive();
143
142
  return cleanup;
144
143
  });
145
- const registered = new WeakSet();
146
- const attached = new Map();
147
- const attach = (agent) => {
148
- if (this.stopping || registered.has(agent) || !ctx.agents.roots().includes(agent))
149
- return;
150
- registered.add(agent);
151
- // The plugin-scope effect is what tears the Agent-scoped registration down when this
152
- // plugin unloads, so it must also be disposed when the Agent itself is released.
153
- attached.set(agent, ctx.effect(() => agent.ctx.effect(() => registerScheduleTools(ctx, agent.ctx, agent))));
154
- };
155
- ctx.on('agent/created', ({ agent }) => { attach(agent); });
156
- ctx.on('agent/disposed', ({ agent }) => {
157
- const detach = attached.get(agent);
158
- if (detach === undefined)
159
- return;
160
- attached.delete(agent);
161
- // `agent/disposed` declares a void listener, so the disposer promise is not returned;
162
- // this teardown chain is synchronous, and a failure throws into
163
- // `AgentRegistry.emitDisposed`, which reports it as a listener throw.
164
- void detach();
165
- });
166
144
  ctx.on('session/created', (session) => {
167
145
  // Historical Schedule events remain readable but do not populate Host tasks.
168
146
  // A throwing `session/created` listener rolls the attach back, so an unreadable
@@ -207,8 +185,6 @@ let ScheduleService = (() => {
207
185
  activity();
208
186
  };
209
187
  }, 'schedule.archiveAdmission()');
210
- for (const agent of ctx.agents.roots())
211
- attach(agent);
212
188
  }
213
189
  async [Service.init]() {
214
190
  await this.initialized;
@@ -218,6 +194,8 @@ let ScheduleService = (() => {
218
194
  *
219
195
  * The request must supply a title; a missing, blank-after-trim, or over-long
220
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.
221
199
  * The record is built from the clock reading taken before the request joins the
222
200
  * serialized queue, so a create that waits behind a longer operation keeps its
223
201
  * request-time anchor and may already be due when the queue reaches it.
@@ -254,6 +232,9 @@ let ScheduleService = (() => {
254
232
  else
255
233
  throw new ScheduleInputError('invalid_selector', 'Exactly one reminder selector is required.');
256
234
  return this.serialize(async () => {
235
+ const refusal = this.reminderTargetRefusal(sessionId);
236
+ if (refusal !== undefined)
237
+ throw new ScheduleInputError('subagent_session', refusal.message);
257
238
  const domain = await this.getDomain();
258
239
  signal?.throwIfAborted();
259
240
  await domain.table('tasks').put(id, {
@@ -311,7 +292,9 @@ let ScheduleService = (() => {
311
292
  * Delete one task belonging to the selected Session, leaving queued messages intact.
312
293
  *
313
294
  * The row is removed: the task no longer schedules, leaves `list` and `catalog`, and its
314
- * 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.
315
298
  * @param request - Session and exact task identity.
316
299
  * @param signal - Optional cancellation checked before persistence begins, including after FIFO waits.
317
300
  * @returns Whether that Session owned a deleted task. Cancellation does not roll back an in-flight write.
@@ -335,14 +318,20 @@ let ScheduleService = (() => {
335
318
  * binding without activating the Session or changing saved deliveries.
336
319
  *
337
320
  * Each supplied field replaces its stored value; an omitted field keeps it. A name or
338
- * 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.
339
325
  * @param request - Task binding, complete observed record, and any combination of timing, name, and instruction.
340
326
  * @param signal - Cancellation checked after domain readiness and FIFO waits, before persistence begins.
341
- * @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.
342
328
  * Storage and lifecycle failures reject; cancellation after a write starts does not roll it back.
343
329
  */
344
330
  async update(request, signal) {
345
331
  return this.serialize(async () => {
332
+ const refusal = this.reminderTargetRefusal(request.sessionId);
333
+ if (refusal !== undefined)
334
+ return refusal;
346
335
  const tasks = (await this.getDomain()).table('tasks');
347
336
  signal?.throwIfAborted();
348
337
  const current = tasks.get(request.id);
@@ -381,6 +370,27 @@ let ScheduleService = (() => {
381
370
  await this.initialized;
382
371
  return this.ready;
383
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
+ }
384
394
  /**
385
395
  * Remove every active task stored for one Session, inside the queue the tools
386
396
  * use.
@@ -265,13 +265,18 @@ export interface FrequencyTooHighError {
265
265
  readonly code: 'frequency_too_high';
266
266
  readonly message: string;
267
267
  }
268
+ /** Stable error returned when the target Session belongs to subagent routing, which never receives reminder delivery. */
269
+ export interface SubagentSessionError {
270
+ readonly code: 'subagent_session';
271
+ readonly message: string;
272
+ }
268
273
  /** Stable fallback that does not disclose an internal exception. */
269
274
  export interface InternalScheduleError {
270
275
  readonly code: 'internal_error';
271
276
  readonly message: string;
272
277
  }
273
278
  /** Closed v1 Schedule management error union. */
274
- export type ScheduleToolError = InvalidPromptError | InvalidSelectorError | InvalidRuleError | InvalidTimeZoneError | NotFutureError | TimeOutOfRangeError | FrequencyTooHighError | InternalScheduleError;
279
+ export type ScheduleToolError = InvalidPromptError | InvalidSelectorError | InvalidRuleError | InvalidTimeZoneError | NotFutureError | TimeOutOfRangeError | FrequencyTooHighError | SubagentSessionError | InternalScheduleError;
275
280
  /** Canonical `schedule_create` value. */
276
281
  export type ScheduleCreateValue = ScheduleView | ScheduleToolError;
277
282
  /** Canonical `schedule_list` value. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-schedule",
3
3
  "description": "Host-wide durable reminders with shared management and original-Session delivery",
4
- "version": "0.2.0-rc.2",
4
+ "version": "0.2.1-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -18,10 +18,6 @@
18
18
  "types": "./lib/types/index.d.ts",
19
19
  "default": "./lib/index.js"
20
20
  },
21
- "./invariant": {
22
- "types": "./lib/types/invariant.d.ts",
23
- "default": "./lib/invariant.js"
24
- },
25
21
  "./client": {
26
22
  "types": "./lib/types/client.d.ts",
27
23
  "default": "./lib/types/client.js"
@@ -41,7 +37,6 @@
41
37
  "files": [
42
38
  "locale/*.json",
43
39
  "lib/index.js",
44
- "lib/invariant.js",
45
40
  "lib/types/**/*.js",
46
41
  "lib/types/**/*.d.ts",
47
42
  "lib/typert.host.js",
@@ -51,45 +46,45 @@
51
46
  ],
52
47
  "license": "MIT",
53
48
  "peerDependencies": {
54
- "@deepseek-ai/dsh-api-session-controller": "0.2.0-rc.2",
55
- "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
56
- "@deepseek-ai/dsh-brand": "0.2.0-rc.2",
57
- "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
58
- "@deepseek-ai/dsh-session": "0.2.0-rc.2",
59
- "@deepseek-ai/dsh-invariants": "0.2.0-rc.2",
60
- "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
61
- "@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
62
- "@deepseek-ai/dsh-storage-domain": "0.2.0-rc.2",
63
- "@deepseek-ai/dsh-session-projection": "0.2.0-rc.2",
64
- "@deepseek-ai/dsh-workspace": "0.2.0-rc.2",
65
- "@deepseek-ai/cordis": "~4.0.4",
66
- "@deepseek-ai/dsh-session-persistence": "0.2.0-rc.2"
49
+ "@deepseek-ai/dsh-agent": "0.2.1-alpha.1",
50
+ "@deepseek-ai/dsh-api-session-controller": "0.2.1-alpha.1",
51
+ "@deepseek-ai/dsh-brand": "0.2.1-alpha.1",
52
+ "@deepseek-ai/dsh-llm": "0.2.1-alpha.1",
53
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.1",
54
+ "@deepseek-ai/dsh-session-persistence": "0.2.1-alpha.1",
55
+ "@deepseek-ai/dsh-session-projection": "0.2.1-alpha.1",
56
+ "@deepseek-ai/dsh-storage-domain": "0.2.1-alpha.1",
57
+ "@deepseek-ai/dsh-subagent": "0.2.1-alpha.1",
58
+ "@deepseek-ai/dsh-tools": "0.2.1-alpha.1",
59
+ "@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.1",
60
+ "@deepseek-ai/dsh-workspace": "0.2.1-alpha.1",
61
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1"
67
62
  },
68
63
  "dependencies": {
69
64
  "@js-temporal/polyfill": "0.5.1",
70
65
  "zod": "^4.4.3",
71
- "@deepseek-ai/schemastery": "~3.18.4"
66
+ "@deepseek-ai/schemastery": "~3.18.5-alpha.1"
72
67
  },
73
68
  "devDependencies": {
74
- "@deepseek-ai/cordis-plugin-loader": "~1.0.5",
75
- "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
76
- "@deepseek-ai/dsh-agent-loop": "0.2.0-rc.2",
77
- "@deepseek-ai/dsh-agent-loop-testkit": "0.2.0-rc.2",
78
- "@deepseek-ai/dsh-api-session-controller": "0.2.0-rc.2",
79
- "@deepseek-ai/dsh-invariants": "0.2.0-rc.2",
80
- "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
81
- "@deepseek-ai/dsh-session": "0.2.0-rc.2",
82
- "@deepseek-ai/dsh-session-persistence": "0.2.0-rc.2",
83
- "@deepseek-ai/dsh-session-persistence-jsonl": "0.2.0-rc.2",
84
- "@deepseek-ai/dsh-session-projection": "0.2.0-rc.2",
85
- "@deepseek-ai/dsh-storage": "0.2.0-rc.2",
86
- "@deepseek-ai/dsh-storage-domain": "0.2.0-rc.2",
87
- "@deepseek-ai/dsh-storage-json": "0.2.0-rc.2",
88
- "@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
89
- "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
90
- "@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
91
- "@deepseek-ai/dsh-workspace": "0.2.0-rc.2",
92
- "@deepseek-ai/cordis": "~4.0.4",
93
- "@deepseek-ai/dsh-brand": "0.2.0-rc.2"
69
+ "@deepseek-ai/cordis-plugin-loader": "~1.0.6-alpha.1",
70
+ "@deepseek-ai/dsh-agent": "0.2.1-alpha.1",
71
+ "@deepseek-ai/dsh-agent-loop": "0.2.1-alpha.1",
72
+ "@deepseek-ai/dsh-agent-loop-testkit": "0.2.1-alpha.1",
73
+ "@deepseek-ai/dsh-api-session-controller": "0.2.1-alpha.1",
74
+ "@deepseek-ai/dsh-brand": "0.2.1-alpha.1",
75
+ "@deepseek-ai/dsh-llm": "0.2.1-alpha.1",
76
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.1",
77
+ "@deepseek-ai/dsh-session-persistence": "0.2.1-alpha.1",
78
+ "@deepseek-ai/dsh-session-persistence-jsonl": "0.2.1-alpha.1",
79
+ "@deepseek-ai/dsh-session-projection": "0.2.1-alpha.1",
80
+ "@deepseek-ai/dsh-storage": "0.2.1-alpha.1",
81
+ "@deepseek-ai/dsh-storage-domain": "0.2.1-alpha.1",
82
+ "@deepseek-ai/dsh-storage-json": "0.2.1-alpha.1",
83
+ "@deepseek-ai/dsh-system-prompt": "0.2.1-alpha.1",
84
+ "@deepseek-ai/dsh-tools": "0.2.1-alpha.1",
85
+ "@deepseek-ai/dsh-workspace": "0.2.1-alpha.1",
86
+ "@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.1",
87
+ "@deepseek-ai/dsh-subagent": "0.2.1-alpha.1",
88
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1"
94
89
  }
95
90
  }