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