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

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/lib/index.js CHANGED
@@ -2,10 +2,10 @@ import { randomUUID } from "node:crypto";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { Service } from "@deepseek-ai/cordis";
4
4
  import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
5
+ import { delegationDepthOf } from "@deepseek-ai/dsh-subagent";
5
6
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
6
7
  import { Temporal } from "@js-temporal/polyfill";
7
8
  import { SessionId, SessionLogOffset } from "@deepseek-ai/dsh-session";
8
- import { defineTool } from "@deepseek-ai/dsh-tools";
9
9
  import { z as z$1 } from "zod";
10
10
  import { MessageId } from "@deepseek-ai/dsh-llm/brand";
11
11
  import { defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain";
@@ -42,7 +42,7 @@ var ScheduleLogError = class extends Error {
42
42
  this.name = "ScheduleLogError";
43
43
  }
44
44
  };
45
- /** Error from a model-supplied Schedule rule that cannot become a record. */
45
+ /** Error from a model-supplied Schedule rule or target Session that cannot become a record. */
46
46
  var ScheduleInputError = class extends Error {
47
47
  /** Stable public Schedule input code. */
48
48
  code;
@@ -1647,627 +1647,6 @@ var ScheduleRuntime = class {
1647
1647
  }
1648
1648
  };
1649
1649
  //#endregion
1650
- //#region lib/types/tools.js
1651
- /**
1652
- * Agent-scoped consumers of the shared Host Schedule management service.
1653
- * @module @deepseek-ai/dsh-schedule
1654
- */
1655
- const SHARED_VIEW_PROPERTIES = {
1656
- id: {
1657
- type: "string",
1658
- required: true
1659
- },
1660
- title: {
1661
- type: "string",
1662
- required: true
1663
- },
1664
- prompt: {
1665
- type: "string",
1666
- required: true
1667
- },
1668
- scheduledAt: {
1669
- type: "string",
1670
- required: true
1671
- },
1672
- state: {
1673
- type: "string",
1674
- required: true,
1675
- enum: ["scheduled", "overdue"]
1676
- },
1677
- deliveryMode: {
1678
- type: "string",
1679
- required: true,
1680
- const: "host"
1681
- }
1682
- };
1683
- const VIEW_SCHEMA = { oneOf: [
1684
- {
1685
- type: "object",
1686
- additionalProperties: false,
1687
- properties: {
1688
- ...SHARED_VIEW_PROPERTIES,
1689
- kind: {
1690
- type: "string",
1691
- required: true,
1692
- const: "after"
1693
- },
1694
- afterSeconds: {
1695
- type: "integer",
1696
- required: true
1697
- }
1698
- }
1699
- },
1700
- {
1701
- type: "object",
1702
- additionalProperties: false,
1703
- properties: {
1704
- ...SHARED_VIEW_PROPERTIES,
1705
- kind: {
1706
- type: "string",
1707
- required: true,
1708
- const: "at"
1709
- }
1710
- }
1711
- },
1712
- {
1713
- type: "object",
1714
- additionalProperties: false,
1715
- properties: {
1716
- ...SHARED_VIEW_PROPERTIES,
1717
- kind: {
1718
- type: "string",
1719
- required: true,
1720
- const: "every"
1721
- },
1722
- everySeconds: {
1723
- type: "integer",
1724
- required: true
1725
- }
1726
- }
1727
- },
1728
- {
1729
- type: "object",
1730
- additionalProperties: false,
1731
- properties: {
1732
- ...SHARED_VIEW_PROPERTIES,
1733
- kind: {
1734
- type: "string",
1735
- required: true,
1736
- const: "daily"
1737
- },
1738
- time: {
1739
- type: "string",
1740
- required: true
1741
- },
1742
- timeZone: {
1743
- type: "string",
1744
- required: true
1745
- }
1746
- }
1747
- },
1748
- {
1749
- type: "object",
1750
- additionalProperties: false,
1751
- properties: {
1752
- ...SHARED_VIEW_PROPERTIES,
1753
- kind: {
1754
- type: "string",
1755
- required: true,
1756
- const: "weekly"
1757
- },
1758
- time: {
1759
- type: "string",
1760
- required: true
1761
- },
1762
- timeZone: {
1763
- type: "string",
1764
- required: true
1765
- },
1766
- weekdays: {
1767
- type: "array",
1768
- required: true,
1769
- items: { type: "integer" }
1770
- }
1771
- }
1772
- },
1773
- {
1774
- type: "object",
1775
- additionalProperties: false,
1776
- properties: {
1777
- ...SHARED_VIEW_PROPERTIES,
1778
- kind: {
1779
- type: "string",
1780
- required: true,
1781
- const: "cron"
1782
- },
1783
- expression: {
1784
- type: "string",
1785
- required: true
1786
- },
1787
- timeZone: {
1788
- type: "string",
1789
- required: true
1790
- }
1791
- }
1792
- }
1793
- ] };
1794
- /** Build one exact two-field error schema while preserving its literal code. */
1795
- function basicErrorSchema(code) {
1796
- return {
1797
- type: "object",
1798
- additionalProperties: false,
1799
- properties: {
1800
- code: {
1801
- type: "string",
1802
- required: true,
1803
- const: code
1804
- },
1805
- message: {
1806
- type: "string",
1807
- required: true
1808
- }
1809
- }
1810
- };
1811
- }
1812
- const ERROR_SCHEMAS = [
1813
- basicErrorSchema("invalid_prompt"),
1814
- basicErrorSchema("invalid_selector"),
1815
- basicErrorSchema("invalid_rule"),
1816
- basicErrorSchema("invalid_time_zone"),
1817
- basicErrorSchema("not_future"),
1818
- basicErrorSchema("time_out_of_range"),
1819
- basicErrorSchema("frequency_too_high"),
1820
- basicErrorSchema("internal_error")
1821
- ];
1822
- const CREATE_OUTPUT_SCHEMA = { oneOf: [VIEW_SCHEMA, ...ERROR_SCHEMAS] };
1823
- const LIST_OUTPUT_SCHEMA = { oneOf: [{
1824
- type: "array",
1825
- items: VIEW_SCHEMA
1826
- }, ...ERROR_SCHEMAS] };
1827
- const DELETE_OUTPUT_SCHEMA = { oneOf: [
1828
- {
1829
- type: "object",
1830
- additionalProperties: false,
1831
- properties: {
1832
- id: {
1833
- type: "string",
1834
- required: true
1835
- },
1836
- deleted: {
1837
- type: "boolean",
1838
- required: true,
1839
- const: true
1840
- }
1841
- }
1842
- },
1843
- {
1844
- type: "object",
1845
- additionalProperties: false,
1846
- properties: {
1847
- id: {
1848
- type: "string",
1849
- required: true
1850
- },
1851
- deleted: {
1852
- type: "boolean",
1853
- required: true,
1854
- const: false
1855
- },
1856
- code: {
1857
- type: "string",
1858
- required: true,
1859
- const: "schedule_not_found"
1860
- }
1861
- }
1862
- },
1863
- ...ERROR_SCHEMAS
1864
- ] };
1865
- const UPDATE_OUTPUT_SCHEMA = { oneOf: [
1866
- VIEW_SCHEMA,
1867
- {
1868
- type: "object",
1869
- additionalProperties: false,
1870
- properties: {
1871
- id: {
1872
- type: "string",
1873
- required: true
1874
- },
1875
- updated: {
1876
- type: "boolean",
1877
- required: true,
1878
- const: false
1879
- },
1880
- code: {
1881
- type: "string",
1882
- required: true,
1883
- enum: [
1884
- "schedule_not_found",
1885
- "schedule_ended",
1886
- "schedule_conflict"
1887
- ]
1888
- }
1889
- }
1890
- },
1891
- ...ERROR_SCHEMAS
1892
- ] };
1893
- const CREATE_DESCRIPTION = "Create a reminder in the current session that delivers prompt when it becomes due. Supply exactly one timing parameter: after_seconds, at, every_seconds, daily, weekly, or cron. Local times that do not exist in the zone are skipped; repeated local times fire once, at the earlier instant. After downtime, a recurring reminder delivers only its latest missed occurrence. Delivery can repeat after a crash.";
1894
- const LIST_DESCRIPTION = "List the active reminders in the current session.";
1895
- const DELETE_DESCRIPTION = "Delete a reminder in the current session, active or inactive. Deletion does not retract a reminder message that is already queued.";
1896
- const UPDATE_DESCRIPTION = "Change a reminder in place, keeping its id. Supply a new title, prompt, or at most one timing parameter; omitted fields keep their stored values. To change a relative delay, create a new reminder.";
1897
- /** Deterministic model content for every canonical Schedule value. */
1898
- function renderValue(_args, value) {
1899
- return [{
1900
- type: "text",
1901
- text: JSON.stringify(value)
1902
- }];
1903
- }
1904
- /** Pure generic pending card. */
1905
- function present(title, kind, rawInput) {
1906
- return {
1907
- card: "generic",
1908
- title,
1909
- kind,
1910
- ...rawInput === void 0 ? {} : { rawInput }
1911
- };
1912
- }
1913
- /** Stable error for failures not safe to expose. */
1914
- function internalError() {
1915
- return {
1916
- code: "internal_error",
1917
- message: "The schedule operation failed."
1918
- };
1919
- }
1920
- /** Translate invalid input while withholding internal storage failures. */
1921
- function operationError(error) {
1922
- return error instanceof ScheduleInputError ? {
1923
- code: error.code,
1924
- message: error.message
1925
- } : internalError();
1926
- }
1927
- /** One supplied fixed-rate interval: a safe integer at or above the Host floor, or undefined. */
1928
- function invalidInterval(everySeconds) {
1929
- if (everySeconds === void 0) return void 0;
1930
- if (!Number.isSafeInteger(everySeconds)) return {
1931
- code: "invalid_rule",
1932
- message: "every_seconds must be a safe integer."
1933
- };
1934
- if (everySeconds < 60) return {
1935
- code: "frequency_too_high",
1936
- message: `every_seconds must be at least 60.`
1937
- };
1938
- }
1939
- /** Validate selector constraints that the open parameter root cannot express. */
1940
- function validateCreateArgs(args) {
1941
- 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 {
1942
- code: "invalid_selector",
1943
- message: "schedule_create accepts exactly one of after_seconds, at, every_seconds, daily, weekly, or cron."
1944
- };
1945
- if (args.prompt.trim().length === 0) return {
1946
- code: "invalid_prompt",
1947
- message: "prompt must be non-empty after trimming."
1948
- };
1949
- if (args.title.trim().length === 0) return {
1950
- code: "invalid_prompt",
1951
- message: REQUIRED_TITLE_MESSAGE
1952
- };
1953
- if (args.title.trim().length > 120) return {
1954
- code: "invalid_prompt",
1955
- message: `title must be at most 120 characters.`
1956
- };
1957
- if (args.after_seconds !== void 0 && (!Number.isSafeInteger(args.after_seconds) || args.after_seconds <= 0)) return {
1958
- code: "invalid_rule",
1959
- message: "after_seconds must be a positive safe integer."
1960
- };
1961
- return invalidInterval(args.every_seconds);
1962
- }
1963
- /** Validate the in-place update's selector count, id, and any supplied name, instruction, or interval. */
1964
- function validateUpdateArgs(args) {
1965
- const selectors = [
1966
- args.at !== void 0,
1967
- args.every_seconds !== void 0,
1968
- args.daily !== void 0,
1969
- args.weekly !== void 0,
1970
- args.cron !== void 0
1971
- ].filter(Boolean).length;
1972
- 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 {
1973
- code: "invalid_selector",
1974
- message: "schedule_update accepts at most one of at, every_seconds, daily, weekly, or cron."
1975
- };
1976
- if (args.id.length === 0 || args.id.trim() !== args.id) return {
1977
- code: "invalid_rule",
1978
- message: "schedule_update id must be non-empty without surrounding whitespace."
1979
- };
1980
- if (selectors === 0 && args.title === void 0 && args.prompt === void 0) return {
1981
- code: "invalid_selector",
1982
- message: "schedule_update needs a new title, prompt, or one of at, every_seconds, daily, weekly, or cron."
1983
- };
1984
- if (args.title !== void 0 && args.title.trim().length === 0) return {
1985
- code: "invalid_prompt",
1986
- message: REQUIRED_TITLE_MESSAGE
1987
- };
1988
- if (args.title !== void 0 && args.title.trim().length > 120) return {
1989
- code: "invalid_prompt",
1990
- message: `title must be at most 120 characters.`
1991
- };
1992
- if (args.prompt !== void 0 && args.prompt.trim().length === 0) return {
1993
- code: "invalid_prompt",
1994
- message: "prompt must be non-empty after trimming."
1995
- };
1996
- return invalidInterval(args.every_seconds);
1997
- }
1998
- /** The one timing replacement the update carries, or undefined when the request keeps the committed target. */
1999
- function timingChangeFrom(args) {
2000
- if (args.at !== void 0) return {
2001
- kind: "at",
2002
- at: args.at
2003
- };
2004
- if (args.every_seconds !== void 0) return {
2005
- kind: "every",
2006
- every_seconds: args.every_seconds
2007
- };
2008
- if (args.daily !== void 0) return {
2009
- kind: "daily",
2010
- daily: args.daily
2011
- };
2012
- if (args.weekly !== void 0) return {
2013
- kind: "weekly",
2014
- weekly: args.weekly
2015
- };
2016
- if (args.cron !== void 0) return {
2017
- kind: "cron",
2018
- cron: args.cron
2019
- };
2020
- }
2021
- /**
2022
- * Selector parameters shared by `schedule_create` and `schedule_update`, in the order the
2023
- * generated tool catalog states them.
2024
- */
2025
- const SELECTOR_PARAMETERS = {
2026
- every_seconds: {
2027
- type: "number",
2028
- description: `Fixed-rate interval in whole seconds, at least 60, aligned to the creation time; changing it with schedule_update re-aligns it to the save time.`
2029
- },
2030
- daily: {
2031
- type: "object",
2032
- additionalProperties: false,
2033
- description: "Every day at a local time.",
2034
- properties: {
2035
- time: {
2036
- type: "string",
2037
- required: true,
2038
- description: "HH:mm:ss with optional 1-3 fractional digits, for example 23:00:00."
2039
- },
2040
- time_zone: {
2041
- type: "string",
2042
- required: true,
2043
- description: "UTC or IANA Area/Location, for example Asia/Shanghai."
2044
- }
2045
- }
2046
- },
2047
- weekly: {
2048
- type: "object",
2049
- additionalProperties: false,
2050
- description: "On the given weekdays at a local time.",
2051
- properties: {
2052
- time: {
2053
- type: "string",
2054
- required: true,
2055
- description: "HH:mm:ss with optional 1-3 fractional digits, for example 09:00:00."
2056
- },
2057
- time_zone: {
2058
- type: "string",
2059
- required: true,
2060
- description: "UTC or IANA Area/Location, for example Asia/Shanghai."
2061
- },
2062
- weekdays: {
2063
- type: "array",
2064
- required: true,
2065
- description: "ISO weekdays, Monday 1 through Sunday 7, without repetitions.",
2066
- items: { type: "integer" }
2067
- }
2068
- }
2069
- },
2070
- cron: {
2071
- type: "object",
2072
- additionalProperties: false,
2073
- description: "Five-field Vixie cron expression in a time zone.",
2074
- properties: {
2075
- expression: {
2076
- type: "string",
2077
- required: true,
2078
- 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."
2079
- },
2080
- time_zone: {
2081
- type: "string",
2082
- required: true,
2083
- description: "UTC or IANA Area/Location, for example Asia/Shanghai."
2084
- }
2085
- }
2086
- },
2087
- at: {
2088
- description: "Absolute target: an RFC 3339 date-time with offset, or a local date, time, and IANA time_zone.",
2089
- oneOf: [{ type: "string" }, {
2090
- type: "object",
2091
- additionalProperties: false,
2092
- properties: {
2093
- date: {
2094
- type: "string",
2095
- required: true
2096
- },
2097
- time: {
2098
- type: "string",
2099
- required: true
2100
- },
2101
- time_zone: {
2102
- type: "string",
2103
- required: true
2104
- }
2105
- }
2106
- }]
2107
- }
2108
- };
2109
- /**
2110
- * Register all four Schedule tools in one exact agent scope.
2111
- * @param rootCtx - Host context owning the shared Schedule service.
2112
- * @param toolCtx - Exact agent-scoped context receiving the definitions.
2113
- * @param agent - Exact live owner whose session the tools mutate.
2114
- * @returns Idempotent aggregate disposer for the four registrations.
2115
- */
2116
- function registerScheduleTools(rootCtx, toolCtx, agent) {
2117
- const disposers = [];
2118
- try {
2119
- disposers.push(toolCtx.tools.register(defineTool({
2120
- name: "schedule_create",
2121
- description: CREATE_DESCRIPTION,
2122
- parameters: {
2123
- prompt: {
2124
- type: "string",
2125
- required: true,
2126
- description: "Reminder content to present when the target becomes due."
2127
- },
2128
- title: {
2129
- type: "string",
2130
- required: true,
2131
- description: `Task name of at most 120 characters, shown on the task card and in task lists.`
2132
- },
2133
- after_seconds: {
2134
- type: "number",
2135
- description: "Delay in whole seconds."
2136
- },
2137
- ...SELECTOR_PARAMETERS
2138
- },
2139
- output: {
2140
- schema: CREATE_OUTPUT_SCHEMA,
2141
- render: renderValue
2142
- },
2143
- async execute(args, exec) {
2144
- if (exec.agent !== agent) return internalError();
2145
- const invalid = validateCreateArgs(args);
2146
- if (invalid !== void 0) return invalid;
2147
- if (exec.signal.aborted) return internalError();
2148
- try {
2149
- return scheduleView(await rootCtx.schedule.create(agent.session.id, args, exec.signal), Date.now());
2150
- } catch (error) {
2151
- return operationError(error);
2152
- }
2153
- },
2154
- presentCall: (args) => present("Create reminder", "other", args.prompt)
2155
- })));
2156
- disposers.push(toolCtx.tools.register(defineTool({
2157
- name: "schedule_list",
2158
- description: LIST_DESCRIPTION,
2159
- parameters: {},
2160
- output: {
2161
- schema: LIST_OUTPUT_SCHEMA,
2162
- render: renderValue
2163
- },
2164
- async execute(_args, exec) {
2165
- if (exec.agent !== agent) return internalError();
2166
- if (exec.signal.aborted) return internalError();
2167
- try {
2168
- return (await rootCtx.schedule.list({ sessionId: agent.session.id })).map((record) => scheduleView(record, Date.now()));
2169
- } catch (error) {
2170
- return operationError(error);
2171
- }
2172
- },
2173
- presentCall: () => present("List reminders", "read")
2174
- })));
2175
- disposers.push(toolCtx.tools.register(defineTool({
2176
- name: "schedule_delete",
2177
- description: DELETE_DESCRIPTION,
2178
- parameters: { id: {
2179
- type: "string",
2180
- required: true,
2181
- description: "Schedule id returned by schedule_list."
2182
- } },
2183
- output: {
2184
- schema: DELETE_OUTPUT_SCHEMA,
2185
- render: renderValue
2186
- },
2187
- async execute(args, exec) {
2188
- if (args.id.length === 0 || args.id.trim() !== args.id) return {
2189
- code: "invalid_rule",
2190
- message: "schedule_delete id must be non-empty without surrounding whitespace."
2191
- };
2192
- const id = ScheduleId(args.id);
2193
- if (exec.agent !== agent) return internalError();
2194
- if (exec.signal.aborted) return internalError();
2195
- try {
2196
- return await rootCtx.schedule.delete({
2197
- sessionId: agent.session.id,
2198
- id
2199
- }, exec.signal);
2200
- } catch (error) {
2201
- return operationError(error);
2202
- }
2203
- },
2204
- presentCall: (args) => present("Delete reminder", "other", args.id)
2205
- })));
2206
- disposers.push(toolCtx.tools.register(defineTool({
2207
- name: "schedule_update",
2208
- description: UPDATE_DESCRIPTION,
2209
- parameters: {
2210
- id: {
2211
- type: "string",
2212
- required: true,
2213
- description: "Schedule id returned by schedule_list."
2214
- },
2215
- title: {
2216
- type: "string",
2217
- description: `New task name of at most 120 characters.`
2218
- },
2219
- prompt: {
2220
- type: "string",
2221
- description: "New reminder content."
2222
- },
2223
- ...SELECTOR_PARAMETERS
2224
- },
2225
- output: {
2226
- schema: UPDATE_OUTPUT_SCHEMA,
2227
- render: renderValue
2228
- },
2229
- async execute(args, exec) {
2230
- if (exec.agent !== agent) return internalError();
2231
- const invalid = validateUpdateArgs(args);
2232
- if (invalid !== void 0) return invalid;
2233
- if (exec.signal.aborted) return internalError();
2234
- const id = ScheduleId(args.id);
2235
- try {
2236
- const sessionId = agent.session.id;
2237
- const expected = (await rootCtx.schedule.list({ sessionId })).find((record) => record.id === id);
2238
- if (expected === void 0) return {
2239
- id,
2240
- updated: false,
2241
- code: (await rootCtx.schedule.catalog()).some((entry) => entry.sessionId === sessionId && entry.id === id) ? "schedule_ended" : "schedule_not_found"
2242
- };
2243
- const change = timingChangeFrom(args);
2244
- const result = await rootCtx.schedule.update({
2245
- sessionId,
2246
- id,
2247
- expected,
2248
- ...change === void 0 ? {} : { change },
2249
- ...args.title === void 0 ? {} : { title: args.title },
2250
- ...args.prompt === void 0 ? {} : { prompt: args.prompt }
2251
- }, exec.signal);
2252
- return "record" in result ? scheduleView(result.record, Date.now()) : result;
2253
- } catch (error) {
2254
- return operationError(error);
2255
- }
2256
- },
2257
- presentCall: (args) => present("Update reminder", "other", args.id)
2258
- })));
2259
- } catch (error) {
2260
- for (const dispose of disposers.reverse()) dispose();
2261
- throw error;
2262
- }
2263
- let active = true;
2264
- return () => {
2265
- if (!active) return;
2266
- active = false;
2267
- for (const dispose of disposers.reverse()) dispose();
2268
- };
2269
- }
2270
- //#endregion
2271
1650
  //#region lib/types/storage.js
2272
1651
  /** Durable Host-wide Schedule tasks, independently of Session activation. */
2273
1652
  const recordSchema = z$1.unknown().transform((value, context) => {
@@ -2590,7 +1969,6 @@ let ScheduleService = (() => {
2590
1969
  static inject = [
2591
1970
  "agents",
2592
1971
  "sessions",
2593
- "tools",
2594
1972
  "storageDomain",
2595
1973
  "sessionController",
2596
1974
  "sessionPersistence"
@@ -2650,22 +2028,6 @@ let ScheduleService = (() => {
2650
2028
  this.runtime.requestDrive();
2651
2029
  return cleanup;
2652
2030
  });
2653
- const registered = /* @__PURE__ */ new WeakSet();
2654
- const attached = /* @__PURE__ */ new Map();
2655
- const attach = (agent) => {
2656
- if (this.stopping || registered.has(agent) || !ctx.agents.roots().includes(agent)) return;
2657
- registered.add(agent);
2658
- attached.set(agent, ctx.effect(() => agent.ctx.effect(() => registerScheduleTools(ctx, agent.ctx, agent))));
2659
- };
2660
- ctx.on("agent/created", ({ agent }) => {
2661
- attach(agent);
2662
- });
2663
- ctx.on("agent/disposed", ({ agent }) => {
2664
- const detach = attached.get(agent);
2665
- if (detach === void 0) return;
2666
- attached.delete(agent);
2667
- detach();
2668
- });
2669
2031
  ctx.on("session/created", (session) => {
2670
2032
  let activeLegacy = 0;
2671
2033
  try {
@@ -2699,7 +2061,6 @@ let ScheduleService = (() => {
2699
2061
  activity();
2700
2062
  };
2701
2063
  }, "schedule.archiveAdmission()");
2702
- for (const agent of ctx.agents.roots()) attach(agent);
2703
2064
  }
2704
2065
  async [Service.init]() {
2705
2066
  await this.initialized;
@@ -2709,6 +2070,8 @@ let ScheduleService = (() => {
2709
2070
  *
2710
2071
  * The request must supply a title; a missing, blank-after-trim, or over-long
2711
2072
  * title rejects with `invalid_prompt` instead of deriving one from the prompt.
2073
+ * A Session a delegated child owns rejects with `subagent_session`, because delivery
2074
+ * can never reach it: the child is one whose delegation depth is above zero.
2712
2075
  * The record is built from the clock reading taken before the request joins the
2713
2076
  * serialized queue, so a create that waits behind a longer operation keeps its
2714
2077
  * request-time anchor and may already be due when the queue reaches it.
@@ -2731,6 +2094,8 @@ let ScheduleService = (() => {
2731
2094
  else if (request.cron !== void 0) record = createCronScheduleRecord(id, request.prompt, request.cron, now, title);
2732
2095
  else throw new ScheduleInputError("invalid_selector", "Exactly one reminder selector is required.");
2733
2096
  return this.serialize(async () => {
2097
+ const refusal = this.reminderTargetRefusal(sessionId);
2098
+ if (refusal !== void 0) throw new ScheduleInputError("subagent_session", refusal.message);
2734
2099
  const domain = await this.getDomain();
2735
2100
  signal?.throwIfAborted();
2736
2101
  await domain.table("tasks").put(id, {
@@ -2789,7 +2154,9 @@ let ScheduleService = (() => {
2789
2154
  * Delete one task belonging to the selected Session, leaving queued messages intact.
2790
2155
  *
2791
2156
  * The row is removed: the task no longer schedules, leaves `list` and `catalog`, and its
2792
- * saved delivery records go with it.
2157
+ * saved delivery records go with it. A task bound to a Session a delegated child owns
2158
+ * stays deletable even though creation and timing edits refuse that binding, so a task
2159
+ * stored before that rule existed remains removable.
2793
2160
  * @param request - Session and exact task identity.
2794
2161
  * @param signal - Optional cancellation checked before persistence begins, including after FIFO waits.
2795
2162
  * @returns Whether that Session owned a deleted task. Cancellation does not roll back an in-flight write.
@@ -2818,14 +2185,19 @@ let ScheduleService = (() => {
2818
2185
  * binding without activating the Session or changing saved deliveries.
2819
2186
  *
2820
2187
  * Each supplied field replaces its stored value; an omitted field keeps it. A name or
2821
- * instruction change alone does not reset the committed target.
2188
+ * instruction change alone does not reset the committed target. A Session a delegated
2189
+ * child owns returns the non-mutating `subagent_session` result, so an edit cannot
2190
+ * re-arm a task bound to a Session delivery can never reach, and the Web editor can
2191
+ * explain the refusal through the ordinary result it already renders.
2822
2192
  * @param request - Task binding, complete observed record, and any combination of timing, name, and instruction.
2823
2193
  * @param signal - Cancellation checked after domain readiness and FIFO waits, before persistence begins.
2824
- * @returns The committed record, unchanged record for a no-op, or a non-mutating input/lookup/conflict result.
2194
+ * @returns The committed record, unchanged record for a no-op, or a non-mutating input/lookup/conflict/refusal result.
2825
2195
  * Storage and lifecycle failures reject; cancellation after a write starts does not roll it back.
2826
2196
  */
2827
2197
  async update(request, signal) {
2828
2198
  return this.serialize(async () => {
2199
+ const refusal = this.reminderTargetRefusal(request.sessionId);
2200
+ if (refusal !== void 0) return refusal;
2829
2201
  const tasks = (await this.getDomain()).table("tasks");
2830
2202
  signal?.throwIfAborted();
2831
2203
  const current = tasks.get(request.id);
@@ -2868,6 +2240,26 @@ let ScheduleService = (() => {
2868
2240
  return this.ready;
2869
2241
  }
2870
2242
  /**
2243
+ * Refuse a Session no delivery can reach because a delegated child owns it.
2244
+ *
2245
+ * Delivery resolves the bound Session through `ctx.sessionController.resolveAgent`,
2246
+ * which rejects the live Agent of a delegated child; a stored task for such a Session
2247
+ * would stay permanently overdue and retry on every drive. Both operations that can
2248
+ * arm a delivery read that same fact here instead of restating it: a delegated child
2249
+ * is one whose {@link delegationDepthOf} is above zero, the accounting the delegation
2250
+ * cap itself enforces, which survives a cold resume through the persisted header.
2251
+ * @param sessionId - Session the task would be bound to.
2252
+ * @returns The stable refusal for a delegated child's Session, or undefined when the Session is eligible.
2253
+ */
2254
+ reminderTargetRefusal(sessionId) {
2255
+ const agent = this.ctx.agents.get(sessionId);
2256
+ if (agent === void 0 || delegationDepthOf(agent) === 0) return void 0;
2257
+ return {
2258
+ code: "subagent_session",
2259
+ message: "This Session belongs to subagent routing, which never receives reminder delivery."
2260
+ };
2261
+ }
2262
+ /**
2871
2263
  * Remove every active task stored for one Session, inside the queue the tools
2872
2264
  * use.
2873
2265
  *
@@ -2907,4 +2299,4 @@ let ScheduleService = (() => {
2907
2299
  };
2908
2300
  })();
2909
2301
  //#endregion
2910
- export { MAX_TITLE_LENGTH, MIN_EVERY_INTERVAL_SECONDS, SCHEDULE_CHANGE_VERSION, ScheduleId, ScheduleInputError, ScheduleLogError, ScheduleService, ScheduleService as default, canonicalizeCronExpression, createAfterScheduleRecord, createAtScheduleRecord, createCronScheduleRecord, createDailyScheduleRecord, createEveryScheduleRecord, createWeeklyScheduleRecord, decodeScheduleChange, decodeScheduleRecord, foldScheduleEvents, isRecurringScheduleRecord, normalizeWeekdays, parseCronInput, parseWeeklyInput, registerScheduleTools, renderRecurringReminderBatchFraming, renderReminderFraming, resolveCronOccurrence, resolveDailyOccurrence, resolveEveryOccurrence, resolveRecurringOccurrence, resolveWeeklyOccurrence, scheduleDomain, scheduleTitle, scheduleView, weeklyTime };
2302
+ export { MAX_TITLE_LENGTH, MIN_EVERY_INTERVAL_SECONDS, REQUIRED_TITLE_MESSAGE, SCHEDULE_CHANGE_VERSION, ScheduleId, ScheduleInputError, ScheduleLogError, ScheduleService, ScheduleService as default, canonicalizeCronExpression, createAfterScheduleRecord, createAtScheduleRecord, createCronScheduleRecord, createDailyScheduleRecord, createEveryScheduleRecord, createWeeklyScheduleRecord, decodeScheduleChange, decodeScheduleRecord, foldScheduleEvents, isRecurringScheduleRecord, normalizeWeekdays, parseCronInput, parseWeeklyInput, renderRecurringReminderBatchFraming, renderReminderFraming, resolveCronOccurrence, resolveDailyOccurrence, resolveEveryOccurrence, resolveRecurringOccurrence, resolveWeeklyOccurrence, scheduleDomain, scheduleTitle, scheduleView, weeklyTime };