@elizaos/plugin-scheduling 2.0.3-beta.5 → 2.0.3-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/anchors/anchor-registry.d.ts +33 -0
- package/dist/anchors/anchor-registry.d.ts.map +1 -0
- package/dist/anchors/anchor-registry.js +129 -0
- package/dist/anchors/anchor-registry.js.map +1 -0
- package/dist/dispatch-types.d.ts +28 -0
- package/dist/dispatch-types.d.ts.map +1 -0
- package/dist/dispatch-types.js +1 -0
- package/dist/dispatch-types.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +17 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +8 -0
- package/dist/plugin.js.map +1 -0
- package/dist/scheduled-task/completion-check-registry.d.ts +19 -0
- package/dist/scheduled-task/completion-check-registry.d.ts.map +1 -0
- package/dist/scheduled-task/completion-check-registry.js +113 -0
- package/dist/scheduled-task/completion-check-registry.js.map +1 -0
- package/dist/scheduled-task/consolidation-policy.d.ts +51 -0
- package/dist/scheduled-task/consolidation-policy.d.ts.map +1 -0
- package/dist/scheduled-task/consolidation-policy.js +154 -0
- package/dist/scheduled-task/consolidation-policy.js.map +1 -0
- package/dist/scheduled-task/due.d.ts +19 -0
- package/dist/scheduled-task/due.d.ts.map +1 -0
- package/dist/scheduled-task/due.js +349 -0
- package/dist/scheduled-task/due.js.map +1 -0
- package/dist/scheduled-task/escalation.d.ts +55 -0
- package/dist/scheduled-task/escalation.d.ts.map +1 -0
- package/dist/scheduled-task/escalation.js +99 -0
- package/dist/scheduled-task/escalation.js.map +1 -0
- package/dist/scheduled-task/gate-registry.d.ts +18 -0
- package/dist/scheduled-task/gate-registry.d.ts.map +1 -0
- package/dist/scheduled-task/gate-registry.js +244 -0
- package/dist/scheduled-task/gate-registry.js.map +1 -0
- package/dist/scheduled-task/index.d.ts +20 -0
- package/dist/scheduled-task/index.d.ts.map +1 -0
- package/dist/scheduled-task/index.js +83 -0
- package/dist/scheduled-task/index.js.map +1 -0
- package/dist/scheduled-task/next-fire-at.d.ts +40 -0
- package/dist/scheduled-task/next-fire-at.d.ts.map +1 -0
- package/dist/scheduled-task/next-fire-at.js +202 -0
- package/dist/scheduled-task/next-fire-at.js.map +1 -0
- package/dist/scheduled-task/runner.d.ts +263 -0
- package/dist/scheduled-task/runner.d.ts.map +1 -0
- package/dist/scheduled-task/runner.js +721 -0
- package/dist/scheduled-task/runner.js.map +1 -0
- package/dist/scheduled-task/state-log.d.ts +56 -0
- package/dist/scheduled-task/state-log.d.ts.map +1 -0
- package/dist/scheduled-task/state-log.js +87 -0
- package/dist/scheduled-task/state-log.js.map +1 -0
- package/dist/scheduled-task/types.d.ts +368 -0
- package/dist/scheduled-task/types.d.ts.map +1 -0
- package/dist/scheduled-task/types.js +14 -0
- package/dist/scheduled-task/types.js.map +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical AnchorRegistry binding for app-lifeops.
|
|
3
|
+
*
|
|
4
|
+
* The `AnchorRegistry` interface itself + the in-memory factory live in
|
|
5
|
+
* `../scheduled-task/consolidation-policy.ts`. This module:
|
|
6
|
+
* 1. Re-exports the canonical type + factory so app-lifeops has a single
|
|
7
|
+
* `registries/anchor-registry.ts` import surface.
|
|
8
|
+
* 2. Adds per-runtime registration (mirrors `connectorRegistry`) so
|
|
9
|
+
* consumers like `plugin-health` can call
|
|
10
|
+
* `getAnchorRegistry(runtime).register(...)`.
|
|
11
|
+
* 3. Registers the built-in calendar / time-window anchors
|
|
12
|
+
* (`meeting.ended`, `morning.start`, `lunch.start`, `night.start`).
|
|
13
|
+
*
|
|
14
|
+
* `wake.observed`, `wake.confirmed`, `bedtime.target`, `nap.start` are
|
|
15
|
+
* registered by `@elizaos/plugin-health` against this same registry through
|
|
16
|
+
* its `registerHealthAnchors(runtime)` entry point.
|
|
17
|
+
*/
|
|
18
|
+
import type { IAgentRuntime } from "@elizaos/core";
|
|
19
|
+
import type { AnchorRegistry } from "../scheduled-task/consolidation-policy.js";
|
|
20
|
+
import type { AnchorContribution } from "../scheduled-task/types.js";
|
|
21
|
+
export type { AnchorRegistry } from "../scheduled-task/consolidation-policy.js";
|
|
22
|
+
export { createAnchorRegistry } from "../scheduled-task/consolidation-policy.js";
|
|
23
|
+
export type { AnchorContext, AnchorContribution, } from "../scheduled-task/types.js";
|
|
24
|
+
export declare const APP_LIFEOPS_ANCHORS: readonly AnchorContribution[];
|
|
25
|
+
/**
|
|
26
|
+
* Register the built-in calendar / time-window anchors. Idempotent via
|
|
27
|
+
* `override: true` so repeated calls (e.g. test setup) don't throw.
|
|
28
|
+
*/
|
|
29
|
+
export declare function registerAppLifeOpsAnchors(registry: AnchorRegistry): void;
|
|
30
|
+
export declare function registerAnchorRegistry(runtime: IAgentRuntime, registry: AnchorRegistry): void;
|
|
31
|
+
export declare function getAnchorRegistry(runtime: IAgentRuntime): AnchorRegistry | null;
|
|
32
|
+
export declare function __resetAnchorRegistryForTests(runtime: IAgentRuntime): void;
|
|
33
|
+
//# sourceMappingURL=anchor-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchor-registry.d.ts","sourceRoot":"","sources":["../../src/anchors/anchor-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAC;AAChF,OAAO,KAAK,EAEV,kBAAkB,EACnB,MAAM,4BAA4B,CAAC;AAEpC,YAAY,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,YAAY,EACV,aAAa,EACb,kBAAkB,GACnB,MAAM,4BAA4B,CAAC;AAmIpC,eAAO,MAAM,mBAAmB,EAAE,SAAS,kBAAkB,EAK5D,CAAC;AAEF;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAIxE;AAQD,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,cAAc,GACvB,IAAI,CAEN;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,aAAa,GACrB,cAAc,GAAG,IAAI,CAEvB;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAE1E"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { createAnchorRegistry } from "../scheduled-task/consolidation-policy.js";
|
|
2
|
+
function nullableTimeAnchor(args) {
|
|
3
|
+
const { anchorKey, label, windowKey, edge } = args;
|
|
4
|
+
return {
|
|
5
|
+
anchorKey,
|
|
6
|
+
describe: {
|
|
7
|
+
label,
|
|
8
|
+
provider: "@elizaos/plugin-personal-assistant:time-window"
|
|
9
|
+
},
|
|
10
|
+
resolve(context) {
|
|
11
|
+
const tz = context.ownerFacts.timezone ?? "UTC";
|
|
12
|
+
const window = context.ownerFacts[windowKey];
|
|
13
|
+
const value = edge === "start" ? window?.start : window?.end;
|
|
14
|
+
if (!value) return null;
|
|
15
|
+
return resolveLocalHHMM(context.nowIso, value, tz);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function resolveLocalHHMM(nowIso, hhmm, tz) {
|
|
20
|
+
const match = /^([01]\d|2[0-3]):([0-5]\d)$/.exec(hhmm);
|
|
21
|
+
if (!match) return null;
|
|
22
|
+
const hour = Number.parseInt(match[1] ?? "0", 10);
|
|
23
|
+
const minute = Number.parseInt(match[2] ?? "0", 10);
|
|
24
|
+
const formatter = new Intl.DateTimeFormat("en-CA", {
|
|
25
|
+
timeZone: tz,
|
|
26
|
+
year: "numeric",
|
|
27
|
+
month: "2-digit",
|
|
28
|
+
day: "2-digit"
|
|
29
|
+
});
|
|
30
|
+
const parts = formatter.formatToParts(new Date(nowIso));
|
|
31
|
+
const y = Number.parseInt(
|
|
32
|
+
parts.find((p) => p.type === "year")?.value ?? "1970",
|
|
33
|
+
10
|
|
34
|
+
);
|
|
35
|
+
const mo = Number.parseInt(
|
|
36
|
+
parts.find((p) => p.type === "month")?.value ?? "01",
|
|
37
|
+
10
|
|
38
|
+
);
|
|
39
|
+
const d = Number.parseInt(
|
|
40
|
+
parts.find((p) => p.type === "day")?.value ?? "01",
|
|
41
|
+
10
|
|
42
|
+
);
|
|
43
|
+
const localDate = new Date(Date.UTC(y, mo - 1, d, hour, minute, 0));
|
|
44
|
+
const offsetFormatter = new Intl.DateTimeFormat("en-US", {
|
|
45
|
+
timeZone: tz,
|
|
46
|
+
timeZoneName: "longOffset",
|
|
47
|
+
year: "numeric",
|
|
48
|
+
month: "2-digit",
|
|
49
|
+
day: "2-digit",
|
|
50
|
+
hour: "2-digit",
|
|
51
|
+
minute: "2-digit",
|
|
52
|
+
hour12: false
|
|
53
|
+
});
|
|
54
|
+
const tzParts = offsetFormatter.formatToParts(localDate);
|
|
55
|
+
const offsetStr = tzParts.find((p) => p.type === "timeZoneName")?.value ?? "GMT";
|
|
56
|
+
const offsetMatch = /GMT([+-]\d{1,2})(?::?(\d{2}))?/.exec(offsetStr);
|
|
57
|
+
let offsetMinutes = 0;
|
|
58
|
+
if (offsetMatch) {
|
|
59
|
+
const sign = offsetMatch[1]?.startsWith("-") ? -1 : 1;
|
|
60
|
+
const oh = Math.abs(Number.parseInt(offsetMatch[1] ?? "0", 10));
|
|
61
|
+
const om = Number.parseInt(offsetMatch[2] ?? "0", 10);
|
|
62
|
+
offsetMinutes = sign * (oh * 60 + om);
|
|
63
|
+
}
|
|
64
|
+
const atMs = localDate.getTime() - offsetMinutes * 6e4;
|
|
65
|
+
return { atIso: new Date(atMs).toISOString() };
|
|
66
|
+
}
|
|
67
|
+
const morningStartAnchor = nullableTimeAnchor({
|
|
68
|
+
anchorKey: "morning.start",
|
|
69
|
+
label: "Owner morning window start (ownerFact.morningWindow.start)",
|
|
70
|
+
windowKey: "morningWindow",
|
|
71
|
+
edge: "start"
|
|
72
|
+
});
|
|
73
|
+
const nightStartAnchor = nullableTimeAnchor({
|
|
74
|
+
anchorKey: "night.start",
|
|
75
|
+
label: "Owner evening / wind-down window start (ownerFact.eveningWindow.start)",
|
|
76
|
+
windowKey: "eveningWindow",
|
|
77
|
+
edge: "start"
|
|
78
|
+
});
|
|
79
|
+
const lunchStartAnchor = {
|
|
80
|
+
anchorKey: "lunch.start",
|
|
81
|
+
describe: {
|
|
82
|
+
label: "Owner lunch window start (default 12:00 local)",
|
|
83
|
+
provider: "@elizaos/plugin-personal-assistant:time-window"
|
|
84
|
+
},
|
|
85
|
+
resolve(context) {
|
|
86
|
+
const tz = context.ownerFacts.timezone ?? "UTC";
|
|
87
|
+
return resolveLocalHHMM(context.nowIso, "12:00", tz);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const meetingEndedAnchor = {
|
|
91
|
+
anchorKey: "meeting.ended",
|
|
92
|
+
describe: {
|
|
93
|
+
label: "Calendar meeting ended (event-driven; resolves via bus)",
|
|
94
|
+
provider: "@elizaos/plugin-personal-assistant:calendar"
|
|
95
|
+
},
|
|
96
|
+
resolve() {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const APP_LIFEOPS_ANCHORS = [
|
|
101
|
+
morningStartAnchor,
|
|
102
|
+
lunchStartAnchor,
|
|
103
|
+
nightStartAnchor,
|
|
104
|
+
meetingEndedAnchor
|
|
105
|
+
];
|
|
106
|
+
function registerAppLifeOpsAnchors(registry) {
|
|
107
|
+
for (const anchor of APP_LIFEOPS_ANCHORS) {
|
|
108
|
+
registry.register(anchor, { override: true });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const registries = /* @__PURE__ */ new WeakMap();
|
|
112
|
+
function registerAnchorRegistry(runtime, registry) {
|
|
113
|
+
registries.set(runtime, registry);
|
|
114
|
+
}
|
|
115
|
+
function getAnchorRegistry(runtime) {
|
|
116
|
+
return registries.get(runtime) ?? null;
|
|
117
|
+
}
|
|
118
|
+
function __resetAnchorRegistryForTests(runtime) {
|
|
119
|
+
registries.delete(runtime);
|
|
120
|
+
}
|
|
121
|
+
export {
|
|
122
|
+
APP_LIFEOPS_ANCHORS,
|
|
123
|
+
__resetAnchorRegistryForTests,
|
|
124
|
+
createAnchorRegistry,
|
|
125
|
+
getAnchorRegistry,
|
|
126
|
+
registerAnchorRegistry,
|
|
127
|
+
registerAppLifeOpsAnchors
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=anchor-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/anchors/anchor-registry.ts"],"sourcesContent":["/**\n * Canonical AnchorRegistry binding for app-lifeops.\n *\n * The `AnchorRegistry` interface itself + the in-memory factory live in\n * `../scheduled-task/consolidation-policy.ts`. This module:\n * 1. Re-exports the canonical type + factory so app-lifeops has a single\n * `registries/anchor-registry.ts` import surface.\n * 2. Adds per-runtime registration (mirrors `connectorRegistry`) so\n * consumers like `plugin-health` can call\n * `getAnchorRegistry(runtime).register(...)`.\n * 3. Registers the built-in calendar / time-window anchors\n * (`meeting.ended`, `morning.start`, `lunch.start`, `night.start`).\n *\n * `wake.observed`, `wake.confirmed`, `bedtime.target`, `nap.start` are\n * registered by `@elizaos/plugin-health` against this same registry through\n * its `registerHealthAnchors(runtime)` entry point.\n */\n\nimport type { IAgentRuntime } from \"@elizaos/core\";\nimport type { AnchorRegistry } from \"../scheduled-task/consolidation-policy.js\";\nimport type {\n AnchorContext,\n AnchorContribution,\n} from \"../scheduled-task/types.js\";\n\nexport type { AnchorRegistry } from \"../scheduled-task/consolidation-policy.js\";\nexport { createAnchorRegistry } from \"../scheduled-task/consolidation-policy.js\";\nexport type {\n AnchorContext,\n AnchorContribution,\n} from \"../scheduled-task/types.js\";\n\n// Built-in anchor contributions (calendar + time windows).\n\nfunction nullableTimeAnchor(args: {\n anchorKey: string;\n label: string;\n windowKey: \"morningWindow\" | \"eveningWindow\";\n edge: \"start\" | \"end\";\n}): AnchorContribution {\n const { anchorKey, label, windowKey, edge } = args;\n return {\n anchorKey,\n describe: {\n label,\n provider: \"@elizaos/plugin-personal-assistant:time-window\",\n },\n resolve(context: AnchorContext) {\n const tz = context.ownerFacts.timezone ?? \"UTC\";\n const window = context.ownerFacts[windowKey];\n const value = edge === \"start\" ? window?.start : window?.end;\n if (!value) return null;\n return resolveLocalHHMM(context.nowIso, value, tz);\n },\n };\n}\n\nfunction resolveLocalHHMM(\n nowIso: string,\n hhmm: string,\n tz: string,\n): { atIso: string } | null {\n const match = /^([01]\\d|2[0-3]):([0-5]\\d)$/.exec(hhmm);\n if (!match) return null;\n const hour = Number.parseInt(match[1] ?? \"0\", 10);\n const minute = Number.parseInt(match[2] ?? \"0\", 10);\n const formatter = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone: tz,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n });\n const parts = formatter.formatToParts(new Date(nowIso));\n const y = Number.parseInt(\n parts.find((p) => p.type === \"year\")?.value ?? \"1970\",\n 10,\n );\n const mo = Number.parseInt(\n parts.find((p) => p.type === \"month\")?.value ?? \"01\",\n 10,\n );\n const d = Number.parseInt(\n parts.find((p) => p.type === \"day\")?.value ?? \"01\",\n 10,\n );\n const localDate = new Date(Date.UTC(y, mo - 1, d, hour, minute, 0));\n const offsetFormatter = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: tz,\n timeZoneName: \"longOffset\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false,\n });\n const tzParts = offsetFormatter.formatToParts(localDate);\n const offsetStr =\n tzParts.find((p) => p.type === \"timeZoneName\")?.value ?? \"GMT\";\n const offsetMatch = /GMT([+-]\\d{1,2})(?::?(\\d{2}))?/.exec(offsetStr);\n let offsetMinutes = 0;\n if (offsetMatch) {\n const sign = offsetMatch[1]?.startsWith(\"-\") ? -1 : 1;\n const oh = Math.abs(Number.parseInt(offsetMatch[1] ?? \"0\", 10));\n const om = Number.parseInt(offsetMatch[2] ?? \"0\", 10);\n offsetMinutes = sign * (oh * 60 + om);\n }\n const atMs = localDate.getTime() - offsetMinutes * 60_000;\n return { atIso: new Date(atMs).toISOString() };\n}\n\nconst morningStartAnchor: AnchorContribution = nullableTimeAnchor({\n anchorKey: \"morning.start\",\n label: \"Owner morning window start (ownerFact.morningWindow.start)\",\n windowKey: \"morningWindow\",\n edge: \"start\",\n});\n\nconst nightStartAnchor: AnchorContribution = nullableTimeAnchor({\n anchorKey: \"night.start\",\n label:\n \"Owner evening / wind-down window start (ownerFact.eveningWindow.start)\",\n windowKey: \"eveningWindow\",\n edge: \"start\",\n});\n\n/**\n * `lunch.start` — local 12:00 in the owner's timezone. There is no\n * `lunchWindow` field on `OwnerFactsView`; this default approximates what\n * the planner currently assumes when scheduling lunch-time prompts.\n */\nconst lunchStartAnchor: AnchorContribution = {\n anchorKey: \"lunch.start\",\n describe: {\n label: \"Owner lunch window start (default 12:00 local)\",\n provider: \"@elizaos/plugin-personal-assistant:time-window\",\n },\n resolve(context) {\n const tz = context.ownerFacts.timezone ?? \"UTC\";\n return resolveLocalHHMM(context.nowIso, \"12:00\", tz);\n },\n};\n\n/**\n * `meeting.ended` — the next concrete resolution time isn't known until a\n * meeting fires (event-driven, not time-driven). The anchor returns `null`\n * here; the calendar emitter publishes a bus event that the runner picks up\n * via `trigger.kind = \"event\"`. The anchor entry exists so the registry\n * lists `meeting.ended` as a known anchor for diagnostics + plan validation.\n */\nconst meetingEndedAnchor: AnchorContribution = {\n anchorKey: \"meeting.ended\",\n describe: {\n label: \"Calendar meeting ended (event-driven; resolves via bus)\",\n provider: \"@elizaos/plugin-personal-assistant:calendar\",\n },\n resolve() {\n return null;\n },\n};\n\nexport const APP_LIFEOPS_ANCHORS: readonly AnchorContribution[] = [\n morningStartAnchor,\n lunchStartAnchor,\n nightStartAnchor,\n meetingEndedAnchor,\n];\n\n/**\n * Register the built-in calendar / time-window anchors. Idempotent via\n * `override: true` so repeated calls (e.g. test setup) don't throw.\n */\nexport function registerAppLifeOpsAnchors(registry: AnchorRegistry): void {\n for (const anchor of APP_LIFEOPS_ANCHORS) {\n registry.register(anchor, { override: true });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Per-runtime registration\n// ---------------------------------------------------------------------------\n\nconst registries = new WeakMap<IAgentRuntime, AnchorRegistry>();\n\nexport function registerAnchorRegistry(\n runtime: IAgentRuntime,\n registry: AnchorRegistry,\n): void {\n registries.set(runtime, registry);\n}\n\nexport function getAnchorRegistry(\n runtime: IAgentRuntime,\n): AnchorRegistry | null {\n return registries.get(runtime) ?? null;\n}\n\nexport function __resetAnchorRegistryForTests(runtime: IAgentRuntime): void {\n registries.delete(runtime);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatch result contract for the scheduling spine.
|
|
3
|
+
*
|
|
4
|
+
* The runner is storage- and transport-agnostic: it only needs the *shape* of a
|
|
5
|
+
* dispatch outcome to drive its dispatch policy (advance-escalation /
|
|
6
|
+
* retry-with-backoff / fail-loud / queue-for-recovery) without inspecting the
|
|
7
|
+
* concrete error. The connector layer that actually sends (owned by the host,
|
|
8
|
+
* e.g. `@elizaos/plugin-personal-assistant`) produces values of this type.
|
|
9
|
+
*
|
|
10
|
+
* Reason taxonomy:
|
|
11
|
+
* - `disconnected` — connector currently has no live session.
|
|
12
|
+
* - `rate_limited` — transport refused due to per-window throttle; SHOULD also
|
|
13
|
+
* populate `retryAfterMinutes`.
|
|
14
|
+
* - `auth_expired` — credentials expired; the user must re-authorize.
|
|
15
|
+
* - `unknown_recipient` — the target identity does not resolve.
|
|
16
|
+
* - `transport_error` — generic infrastructure failure (network, 5xx, timeout).
|
|
17
|
+
*/
|
|
18
|
+
export type DispatchResult = {
|
|
19
|
+
ok: true;
|
|
20
|
+
messageId?: string;
|
|
21
|
+
} | {
|
|
22
|
+
ok: false;
|
|
23
|
+
reason: "disconnected" | "rate_limited" | "auth_expired" | "unknown_recipient" | "transport_error";
|
|
24
|
+
retryAfterMinutes?: number;
|
|
25
|
+
userActionable: boolean;
|
|
26
|
+
message?: string;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=dispatch-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch-types.d.ts","sourceRoot":"","sources":["../src/dispatch-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAChC;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EACF,cAAc,GACd,cAAc,GACd,cAAc,GACd,mBAAmB,GACnB,iBAAiB,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=dispatch-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { __resetAnchorRegistryForTests, APP_LIFEOPS_ANCHORS, getAnchorRegistry, registerAnchorRegistry, registerAppLifeOpsAnchors, } from "./anchors/anchor-registry.js";
|
|
2
|
+
export type { DispatchResult } from "./dispatch-types.js";
|
|
3
|
+
export { schedulingPlugin } from "./plugin.js";
|
|
4
|
+
export * from "./scheduled-task/index.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,6BAA6B,EAC7B,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,cAAc,2BAA2B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__resetAnchorRegistryForTests,
|
|
3
|
+
APP_LIFEOPS_ANCHORS,
|
|
4
|
+
getAnchorRegistry,
|
|
5
|
+
registerAnchorRegistry,
|
|
6
|
+
registerAppLifeOpsAnchors
|
|
7
|
+
} from "./anchors/anchor-registry.js";
|
|
8
|
+
import { schedulingPlugin } from "./plugin.js";
|
|
9
|
+
export * from "./scheduled-task/index.js";
|
|
10
|
+
export {
|
|
11
|
+
APP_LIFEOPS_ANCHORS,
|
|
12
|
+
__resetAnchorRegistryForTests,
|
|
13
|
+
getAnchorRegistry,
|
|
14
|
+
registerAnchorRegistry,
|
|
15
|
+
registerAppLifeOpsAnchors,
|
|
16
|
+
schedulingPlugin
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// The spine barrel (scheduled-task/index) already re-exports AnchorRegistry +\n// createAnchorRegistry from consolidation-policy, so surface only the\n// anchor-registry's own symbols here to avoid a duplicate-export collision.\nexport {\n __resetAnchorRegistryForTests,\n APP_LIFEOPS_ANCHORS,\n getAnchorRegistry,\n registerAnchorRegistry,\n registerAppLifeOpsAnchors,\n} from \"./anchors/anchor-registry.js\";\nexport type { DispatchResult } from \"./dispatch-types.js\";\nexport { schedulingPlugin } from \"./plugin.js\";\nexport * from \"./scheduled-task/index.js\";\n"],"mappings":"AAGA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,wBAAwB;AACjC,cAAc;","names":[]}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Plugin } from "@elizaos/core";
|
|
2
|
+
/**
|
|
3
|
+
* `@elizaos/plugin-scheduling` — the scheduling spine.
|
|
4
|
+
*
|
|
5
|
+
* During the LifeOps decomposition this package owns the storage-agnostic
|
|
6
|
+
* ScheduledTask state machine (types, runner, registries, due/next-fire-at
|
|
7
|
+
* math, anchors) and the spine→reminders ports. Persistence and the
|
|
8
|
+
* owner/channel/connector dependencies are INJECTED by the host
|
|
9
|
+
* (`@elizaos/plugin-personal-assistant`), which remains the registrar of the
|
|
10
|
+
* runner service + the SCHEDULED_TASKS action during the decomposition. This
|
|
11
|
+
* Plugin object is exported for standalone use; the runtime first-wins dedup
|
|
12
|
+
* prevents double-registration when PA also registers the spine.
|
|
13
|
+
*
|
|
14
|
+
* See `plugins/plugin-personal-assistant/docs/lifeops-extraction-plan.md`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const schedulingPlugin: Plugin;
|
|
17
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAI9B,CAAC"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const schedulingPlugin = {
|
|
2
|
+
name: "@elizaos/plugin-scheduling",
|
|
3
|
+
description: "Scheduling spine: the storage-agnostic ScheduledTask state machine + registries + runner. Persistence and owner/channel deps are injected by the host."
|
|
4
|
+
};
|
|
5
|
+
export {
|
|
6
|
+
schedulingPlugin
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type { Plugin } from \"@elizaos/core\";\n\n/**\n * `@elizaos/plugin-scheduling` — the scheduling spine.\n *\n * During the LifeOps decomposition this package owns the storage-agnostic\n * ScheduledTask state machine (types, runner, registries, due/next-fire-at\n * math, anchors) and the spine→reminders ports. Persistence and the\n * owner/channel/connector dependencies are INJECTED by the host\n * (`@elizaos/plugin-personal-assistant`), which remains the registrar of the\n * runner service + the SCHEDULED_TASKS action during the decomposition. This\n * Plugin object is exported for standalone use; the runtime first-wins dedup\n * prevents double-registration when PA also registers the spine.\n *\n * See `plugins/plugin-personal-assistant/docs/lifeops-extraction-plan.md`.\n */\nexport const schedulingPlugin: Plugin = {\n name: \"@elizaos/plugin-scheduling\",\n description:\n \"Scheduling spine: the storage-agnostic ScheduledTask state machine + registries + runner. Persistence and owner/channel deps are injected by the host.\",\n};\n"],"mappings":"AAgBO,MAAM,mBAA2B;AAAA,EACtC,MAAM;AAAA,EACN,aACE;AACJ;","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CompletionCheckRegistry. Built-in kinds:
|
|
3
|
+
* - `user_acknowledged`
|
|
4
|
+
* - `user_replied_within { lookbackMinutes, requireSinceTaskFired }`
|
|
5
|
+
* - `subject_updated`
|
|
6
|
+
* - `health_signal_observed { signalKind, lookbackMinutes, requireSinceTaskFired }`
|
|
7
|
+
*
|
|
8
|
+
* Each check returns a boolean — `true` means the runner moves the task
|
|
9
|
+
* to `completed` (and fires `pipeline.onComplete`).
|
|
10
|
+
*/
|
|
11
|
+
import type { CompletionCheckContribution } from "./types.js";
|
|
12
|
+
export interface CompletionCheckRegistry {
|
|
13
|
+
register(c: CompletionCheckContribution): void;
|
|
14
|
+
get(kind: string): CompletionCheckContribution | null;
|
|
15
|
+
list(): CompletionCheckContribution[];
|
|
16
|
+
}
|
|
17
|
+
export declare function createCompletionCheckRegistry(): CompletionCheckRegistry;
|
|
18
|
+
export declare function registerBuiltInCompletionChecks(reg: CompletionCheckRegistry): void;
|
|
19
|
+
//# sourceMappingURL=completion-check-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion-check-registry.d.ts","sourceRoot":"","sources":["../../src/scheduled-task/completion-check-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAEV,2BAA2B,EAE5B,MAAM,YAAY,CAAC;AAqHpB,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAC/C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,2BAA2B,GAAG,IAAI,CAAC;IACtD,IAAI,IAAI,2BAA2B,EAAE,CAAC;CACvC;AAED,wBAAgB,6BAA6B,IAAI,uBAAuB,CAqBvE;AAED,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,uBAAuB,GAC3B,IAAI,CAKN"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
function paramsForCheck(task, kind) {
|
|
2
|
+
if (task.completionCheck?.kind === kind) {
|
|
3
|
+
return task.completionCheck.params;
|
|
4
|
+
}
|
|
5
|
+
return void 0;
|
|
6
|
+
}
|
|
7
|
+
function isoMinusMinutes(iso, minutes) {
|
|
8
|
+
const t = new Date(iso).getTime();
|
|
9
|
+
const safeMinutes = Number.isFinite(minutes) && minutes > 0 ? minutes : 0;
|
|
10
|
+
return new Date(t - safeMinutes * 6e4).toISOString();
|
|
11
|
+
}
|
|
12
|
+
function resolveSinceIso(context, lookbackMinutes, requireSinceTaskFired) {
|
|
13
|
+
const firedAt = context.task.state.firedAt;
|
|
14
|
+
if (requireSinceTaskFired && firedAt) {
|
|
15
|
+
return firedAt;
|
|
16
|
+
}
|
|
17
|
+
const minutes = Number.isFinite(lookbackMinutes) ? Number(lookbackMinutes) : 60;
|
|
18
|
+
return isoMinusMinutes(context.nowIso, minutes);
|
|
19
|
+
}
|
|
20
|
+
const userAcknowledgedCheck = {
|
|
21
|
+
kind: "user_acknowledged",
|
|
22
|
+
shouldComplete(_task, context) {
|
|
23
|
+
return context.acknowledged === true;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const userRepliedWithinCheck = {
|
|
27
|
+
kind: "user_replied_within",
|
|
28
|
+
shouldComplete(task, context) {
|
|
29
|
+
const params = paramsForCheck(
|
|
30
|
+
task,
|
|
31
|
+
"user_replied_within"
|
|
32
|
+
);
|
|
33
|
+
const lookback = params?.lookbackMinutes;
|
|
34
|
+
const requireSince = params?.requireSinceTaskFired !== false;
|
|
35
|
+
if (!context.repliedSinceFiredAt?.atIso) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const sinceIso = resolveSinceIso(context, lookback, requireSince);
|
|
39
|
+
return new Date(context.repliedSinceFiredAt.atIso).getTime() >= new Date(sinceIso).getTime();
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const subjectUpdatedCheck = {
|
|
43
|
+
kind: "subject_updated",
|
|
44
|
+
async shouldComplete(task, context) {
|
|
45
|
+
if (!task.subject) return false;
|
|
46
|
+
const params = paramsForCheck(task, "subject_updated");
|
|
47
|
+
const lookback = params?.lookbackMinutes;
|
|
48
|
+
const requireSince = params?.requireSinceTaskFired !== false;
|
|
49
|
+
const sinceIso = resolveSinceIso(context, lookback, requireSince);
|
|
50
|
+
const updated = await context.subjectStore.wasUpdatedSince({
|
|
51
|
+
subject: task.subject,
|
|
52
|
+
sinceIso
|
|
53
|
+
});
|
|
54
|
+
return updated === true;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const healthSignalObservedCheck = {
|
|
58
|
+
kind: "health_signal_observed",
|
|
59
|
+
async shouldComplete(task, context) {
|
|
60
|
+
const params = paramsForCheck(
|
|
61
|
+
task,
|
|
62
|
+
"health_signal_observed"
|
|
63
|
+
);
|
|
64
|
+
if (!params || typeof params.signalKind !== "string") {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
const requireSince = params.requireSinceTaskFired !== false;
|
|
68
|
+
const sinceIso = resolveSinceIso(
|
|
69
|
+
context,
|
|
70
|
+
params.lookbackMinutes,
|
|
71
|
+
requireSince
|
|
72
|
+
);
|
|
73
|
+
const observed = await context.activity.hasSignalSince({
|
|
74
|
+
signalKind: params.signalKind,
|
|
75
|
+
sinceIso,
|
|
76
|
+
subject: task.subject
|
|
77
|
+
});
|
|
78
|
+
return observed === true;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
function createCompletionCheckRegistry() {
|
|
82
|
+
const map = /* @__PURE__ */ new Map();
|
|
83
|
+
return {
|
|
84
|
+
register(c) {
|
|
85
|
+
if (!c.kind || typeof c.kind !== "string") {
|
|
86
|
+
throw new Error("CompletionCheckRegistry.register: kind required");
|
|
87
|
+
}
|
|
88
|
+
if (map.has(c.kind)) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`CompletionCheckRegistry.register: duplicate kind "${c.kind}"`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
map.set(c.kind, c);
|
|
94
|
+
},
|
|
95
|
+
get(kind) {
|
|
96
|
+
return map.get(kind) ?? null;
|
|
97
|
+
},
|
|
98
|
+
list() {
|
|
99
|
+
return Array.from(map.values());
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function registerBuiltInCompletionChecks(reg) {
|
|
104
|
+
reg.register(userAcknowledgedCheck);
|
|
105
|
+
reg.register(userRepliedWithinCheck);
|
|
106
|
+
reg.register(subjectUpdatedCheck);
|
|
107
|
+
reg.register(healthSignalObservedCheck);
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
createCompletionCheckRegistry,
|
|
111
|
+
registerBuiltInCompletionChecks
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=completion-check-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/scheduled-task/completion-check-registry.ts"],"sourcesContent":["/**\n * CompletionCheckRegistry. Built-in kinds:\n * - `user_acknowledged`\n * - `user_replied_within { lookbackMinutes, requireSinceTaskFired }`\n * - `subject_updated`\n * - `health_signal_observed { signalKind, lookbackMinutes, requireSinceTaskFired }`\n *\n * Each check returns a boolean — `true` means the runner moves the task\n * to `completed` (and fires `pipeline.onComplete`).\n */\n\nimport type {\n CompletionCheckContext,\n CompletionCheckContribution,\n ScheduledTask,\n} from \"./types.js\";\n\ninterface UserRepliedWithinParams {\n lookbackMinutes?: number;\n /** When true, only inbounds since the most recent fire count. Default true. */\n requireSinceTaskFired?: boolean;\n}\n\ninterface HealthSignalObservedParams {\n signalKind: string;\n lookbackMinutes?: number;\n requireSinceTaskFired?: boolean;\n}\n\nfunction paramsForCheck<T>(task: ScheduledTask, kind: string): T | undefined {\n if (task.completionCheck?.kind === kind) {\n return task.completionCheck.params as T | undefined;\n }\n return undefined;\n}\n\nfunction isoMinusMinutes(iso: string, minutes: number): string {\n const t = new Date(iso).getTime();\n const safeMinutes = Number.isFinite(minutes) && minutes > 0 ? minutes : 0;\n return new Date(t - safeMinutes * 60_000).toISOString();\n}\n\nfunction resolveSinceIso(\n context: CompletionCheckContext,\n lookbackMinutes: number | undefined,\n requireSinceTaskFired: boolean,\n): string {\n const firedAt = context.task.state.firedAt;\n if (requireSinceTaskFired && firedAt) {\n return firedAt;\n }\n const minutes = Number.isFinite(lookbackMinutes)\n ? Number(lookbackMinutes)\n : 60;\n return isoMinusMinutes(context.nowIso, minutes);\n}\n\nconst userAcknowledgedCheck: CompletionCheckContribution = {\n kind: \"user_acknowledged\",\n shouldComplete(_task, context): boolean {\n return context.acknowledged === true;\n },\n};\n\nconst userRepliedWithinCheck: CompletionCheckContribution = {\n kind: \"user_replied_within\",\n shouldComplete(task, context): boolean {\n const params = paramsForCheck<UserRepliedWithinParams>(\n task,\n \"user_replied_within\",\n );\n const lookback = params?.lookbackMinutes;\n const requireSince = params?.requireSinceTaskFired !== false;\n if (!context.repliedSinceFiredAt?.atIso) {\n return false;\n }\n const sinceIso = resolveSinceIso(context, lookback, requireSince);\n return (\n new Date(context.repliedSinceFiredAt.atIso).getTime() >=\n new Date(sinceIso).getTime()\n );\n },\n};\n\nconst subjectUpdatedCheck: CompletionCheckContribution = {\n kind: \"subject_updated\",\n async shouldComplete(task, context): Promise<boolean> {\n if (!task.subject) return false;\n const params = paramsForCheck<{\n lookbackMinutes?: number;\n requireSinceTaskFired?: boolean;\n }>(task, \"subject_updated\");\n const lookback = params?.lookbackMinutes;\n const requireSince = params?.requireSinceTaskFired !== false;\n const sinceIso = resolveSinceIso(context, lookback, requireSince);\n const updated = await context.subjectStore.wasUpdatedSince({\n subject: task.subject,\n sinceIso,\n });\n return updated === true;\n },\n};\n\nconst healthSignalObservedCheck: CompletionCheckContribution = {\n kind: \"health_signal_observed\",\n async shouldComplete(task, context): Promise<boolean> {\n const params = paramsForCheck<HealthSignalObservedParams>(\n task,\n \"health_signal_observed\",\n );\n if (!params || typeof params.signalKind !== \"string\") {\n return false;\n }\n const requireSince = params.requireSinceTaskFired !== false;\n const sinceIso = resolveSinceIso(\n context,\n params.lookbackMinutes,\n requireSince,\n );\n const observed = await context.activity.hasSignalSince({\n signalKind: params.signalKind,\n sinceIso,\n subject: task.subject,\n });\n return observed === true;\n },\n};\n\n// ---------------------------------------------------------------------------\n// Registry\n// ---------------------------------------------------------------------------\n\nexport interface CompletionCheckRegistry {\n register(c: CompletionCheckContribution): void;\n get(kind: string): CompletionCheckContribution | null;\n list(): CompletionCheckContribution[];\n}\n\nexport function createCompletionCheckRegistry(): CompletionCheckRegistry {\n const map = new Map<string, CompletionCheckContribution>();\n return {\n register(c) {\n if (!c.kind || typeof c.kind !== \"string\") {\n throw new Error(\"CompletionCheckRegistry.register: kind required\");\n }\n if (map.has(c.kind)) {\n throw new Error(\n `CompletionCheckRegistry.register: duplicate kind \"${c.kind}\"`,\n );\n }\n map.set(c.kind, c);\n },\n get(kind) {\n return map.get(kind) ?? null;\n },\n list() {\n return Array.from(map.values());\n },\n };\n}\n\nexport function registerBuiltInCompletionChecks(\n reg: CompletionCheckRegistry,\n): void {\n reg.register(userAcknowledgedCheck);\n reg.register(userRepliedWithinCheck);\n reg.register(subjectUpdatedCheck);\n reg.register(healthSignalObservedCheck);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AnchorContribution + AnchorConsolidationPolicy registry.
|
|
3
|
+
*
|
|
4
|
+
* Ships a fallback `wake.confirmed` anchor that resolves to
|
|
5
|
+
* `ownerFact.morningWindow.start`. `plugin-health` registers the richer
|
|
6
|
+
* `wake.observed` / `wake.confirmed` / `bedtime.target` / `nap.start`
|
|
7
|
+
* anchors and may overwrite the fallback at boot — so the fallback is only
|
|
8
|
+
* registered when the real one is absent.
|
|
9
|
+
*
|
|
10
|
+
* Consolidation policies are referenced by anchor key. The runner uses
|
|
11
|
+
* them when multiple `relative_to_anchor` tasks fire on the same anchor;
|
|
12
|
+
* `mode = "merge"` means the runner asks consumers to render one
|
|
13
|
+
* combined card; `sequential` staggers; `parallel` fires all at once.
|
|
14
|
+
*/
|
|
15
|
+
import type { AnchorConsolidationPolicy, AnchorContext, AnchorContribution, ScheduledTask } from "./types.js";
|
|
16
|
+
export interface AnchorRegistry {
|
|
17
|
+
register(a: AnchorContribution, opts?: {
|
|
18
|
+
override?: boolean;
|
|
19
|
+
}): void;
|
|
20
|
+
get(anchorKey: string): AnchorContribution | null;
|
|
21
|
+
list(): AnchorContribution[];
|
|
22
|
+
resolve(anchorKey: string, context: AnchorContext): Promise<{
|
|
23
|
+
atIso: string;
|
|
24
|
+
} | null>;
|
|
25
|
+
}
|
|
26
|
+
export declare function createAnchorRegistry(): AnchorRegistry;
|
|
27
|
+
declare function todayIsoWithLocalHHMM(nowIso: string, hhmm: string, tz: string): {
|
|
28
|
+
atIso: string;
|
|
29
|
+
} | null;
|
|
30
|
+
export declare function registerFallbackAnchors(reg: AnchorRegistry): void;
|
|
31
|
+
export interface ConsolidationRegistry {
|
|
32
|
+
register(p: AnchorConsolidationPolicy): void;
|
|
33
|
+
get(anchorKey: string): AnchorConsolidationPolicy | null;
|
|
34
|
+
list(): AnchorConsolidationPolicy[];
|
|
35
|
+
/**
|
|
36
|
+
* Apply a policy to a fresh batch of tasks fired on the same anchor.
|
|
37
|
+
* Returns batches the runner should hand to its dispatcher; each batch
|
|
38
|
+
* is a list of tasks the consumer renders together (or sequentially —
|
|
39
|
+
* the policy mode tells the consumer how).
|
|
40
|
+
*/
|
|
41
|
+
consolidate(anchorKey: string, tasks: ScheduledTask[]): {
|
|
42
|
+
policy: AnchorConsolidationPolicy | null;
|
|
43
|
+
batches: ScheduledTask[][];
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export declare function createConsolidationRegistry(): ConsolidationRegistry;
|
|
47
|
+
export declare const __anchorTestUtils: {
|
|
48
|
+
todayIsoWithLocalHHMM: typeof todayIsoWithLocalHHMM;
|
|
49
|
+
};
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=consolidation-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consolidation-policy.d.ts","sourceRoot":"","sources":["../../src/scheduled-task/consolidation-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACV,yBAAyB,EACzB,aAAa,EACb,kBAAkB,EAClB,aAAa,EAEd,MAAM,YAAY,CAAC;AAMpB,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,CAAC,EAAE,kBAAkB,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACrE,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAAC;IAClD,IAAI,IAAI,kBAAkB,EAAE,CAAC;IAC7B,OAAO,CACL,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CACtC;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CA2BrD;AAMD,iBAAS,qBAAqB,CAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA2D1B;AAgBD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAIjE;AAMD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAC7C,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,yBAAyB,GAAG,IAAI,CAAC;IACzD,IAAI,IAAI,yBAAyB,EAAE,CAAC;IACpC;;;;;OAKG;IACH,WAAW,CACT,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,aAAa,EAAE,GACrB;QAAE,MAAM,EAAE,yBAAyB,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,aAAa,EAAE,EAAE,CAAA;KAAE,CAAC;CAC7E;AAQD,wBAAgB,2BAA2B,IAAI,qBAAqB,CAoDnE;AAED,eAAO,MAAM,iBAAiB;;CAA4B,CAAC"}
|