@jini-ai/daemon 0.3.7 → 0.5.0

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.
Files changed (159) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +50 -8
  3. package/dist/agent-executor/contracts.d.ts +736 -0
  4. package/dist/agent-executor/contracts.js +28 -0
  5. package/dist/agent-executor/event-translation.d.ts +87 -0
  6. package/dist/agent-executor/event-translation.js +230 -0
  7. package/dist/agent-executor/events.d.ts +190 -0
  8. package/dist/agent-executor/events.js +848 -0
  9. package/dist/agent-executor/handoff.d.ts +18 -0
  10. package/dist/agent-executor/handoff.js +16 -0
  11. package/dist/agent-executor/index.d.ts +98 -0
  12. package/dist/agent-executor/index.js +98 -0
  13. package/dist/agent-executor/launch.d.ts +375 -0
  14. package/dist/agent-executor/launch.js +1001 -0
  15. package/dist/agent-executor/mcp-bridge.d.ts +338 -0
  16. package/dist/agent-executor/mcp-bridge.js +835 -0
  17. package/dist/agent-executor/values.d.ts +5 -0
  18. package/dist/agent-executor/values.js +13 -0
  19. package/dist/agent-executor.d.ts +382 -177
  20. package/dist/agent-executor.js +408 -344
  21. package/dist/close-status.d.ts +11 -8
  22. package/dist/close-status.js +31 -19
  23. package/dist/continuation/continuation-transport.d.ts +3 -1
  24. package/dist/continuation/continuation-transport.js +1 -1
  25. package/dist/continuation/journal.d.ts +12 -5
  26. package/dist/continuation/journal.js +4 -4
  27. package/dist/continuation/run-scoped-context-store.d.ts +13 -4
  28. package/dist/continuation/run-scoped-context-store.js +6 -6
  29. package/dist/continuation/run-start-handler.d.ts +1 -1
  30. package/dist/continuation/run-start-handler.js +3 -16
  31. package/dist/delegated-tool-bridge.d.ts +7 -3
  32. package/dist/delegated-tool-bridge.js +32 -31
  33. package/dist/event-log.d.ts +7 -5
  34. package/dist/event-log.js +12 -11
  35. package/dist/frontend-capability-tools.d.ts +1 -1
  36. package/dist/frontend-capability-tools.js +3 -2
  37. package/dist/frontend-session-registry.d.ts +32 -9
  38. package/dist/frontend-session-registry.js +12 -11
  39. package/dist/http/active-context.d.ts +105 -0
  40. package/dist/http/active-context.js +67 -0
  41. package/dist/http/agents.d.ts +68 -0
  42. package/dist/http/agents.js +12 -0
  43. package/dist/http/attachments.d.ts +618 -0
  44. package/dist/http/attachments.js +1034 -0
  45. package/dist/http/cancel-owned-runs.d.ts +49 -0
  46. package/dist/http/cancel-owned-runs.js +35 -0
  47. package/dist/http/component-catalog.d.ts +84 -0
  48. package/dist/http/component-catalog.js +64 -0
  49. package/dist/http/connectors.d.ts +209 -0
  50. package/dist/http/connectors.js +409 -0
  51. package/dist/http/daemon-status.d.ts +76 -0
  52. package/dist/http/daemon-status.js +55 -0
  53. package/dist/http/db-ops.d.ts +123 -0
  54. package/dist/http/db-ops.js +189 -0
  55. package/dist/http/delegated-tools.d.ts +113 -0
  56. package/dist/http/delegated-tools.js +230 -0
  57. package/dist/http/frontend-control.d.ts +136 -0
  58. package/dist/http/frontend-control.js +79 -0
  59. package/dist/http/frontend-sessions.d.ts +110 -0
  60. package/dist/http/frontend-sessions.js +180 -0
  61. package/dist/http/health.d.ts +95 -0
  62. package/dist/http/health.js +40 -0
  63. package/dist/http/host-tools.d.ts +214 -0
  64. package/dist/http/host-tools.js +329 -0
  65. package/dist/http/index.d.ts +52 -0
  66. package/dist/http/index.js +27 -0
  67. package/dist/http/media.d.ts +146 -0
  68. package/dist/http/media.js +346 -0
  69. package/dist/http/memory.d.ts +265 -0
  70. package/dist/http/memory.js +328 -0
  71. package/dist/http/model-proxy.d.ts +49 -0
  72. package/dist/http/model-proxy.js +642 -0
  73. package/dist/http/remote-run-events.d.ts +70 -0
  74. package/dist/http/remote-run-events.js +224 -0
  75. package/dist/http/research.d.ts +48 -0
  76. package/dist/http/research.js +315 -0
  77. package/dist/http/route-manifest.d.ts +35 -0
  78. package/dist/http/route-manifest.js +103 -0
  79. package/dist/http/routines.d.ts +120 -0
  80. package/dist/http/routines.js +298 -0
  81. package/dist/http/run-credentials.d.ts +73 -0
  82. package/dist/http/run-credentials.js +71 -0
  83. package/dist/http/runs.d.ts +91 -0
  84. package/dist/http/runs.js +221 -0
  85. package/dist/http/terminals.d.ts +138 -0
  86. package/dist/http/terminals.js +325 -0
  87. package/dist/http/tool-catalog.d.ts +78 -0
  88. package/dist/http/tool-catalog.js +66 -0
  89. package/dist/http/workspace-root.d.ts +77 -0
  90. package/dist/http/workspace-root.js +63 -0
  91. package/dist/http/xai.d.ts +90 -0
  92. package/dist/http/xai.js +655 -0
  93. package/dist/image-prompt-delivery.d.ts +13 -3
  94. package/dist/image-prompt-delivery.js +5 -5
  95. package/dist/index.d.ts +556 -4
  96. package/dist/index.js +556 -4
  97. package/dist/legacy-data-migration.d.ts +59 -8
  98. package/dist/legacy-data-migration.js +38 -33
  99. package/dist/read-only-tools.d.ts +130 -0
  100. package/dist/read-only-tools.js +115 -0
  101. package/dist/remote-tool-bridge.d.ts +11 -5
  102. package/dist/remote-tool-bridge.js +15 -15
  103. package/dist/routines/index.d.ts +1 -1
  104. package/dist/routines/index.js +1 -1
  105. package/dist/routines/routine-store.d.ts +34 -12
  106. package/dist/routines/routine-store.js +21 -20
  107. package/dist/routines/schedule.d.ts +48 -10
  108. package/dist/routines/schedule.js +21 -21
  109. package/dist/routines/scheduler.d.ts +32 -11
  110. package/dist/routines/scheduler.js +61 -57
  111. package/dist/routines/types.d.ts +10 -3
  112. package/dist/run/core/result.d.ts +6 -2
  113. package/dist/run/core/result.js +3 -3
  114. package/dist/run/core/retry.d.ts +17 -4
  115. package/dist/run/core/retry.js +7 -11
  116. package/dist/run/diagnostics/diagnostics.d.ts +14 -4
  117. package/dist/run/diagnostics/diagnostics.js +7 -7
  118. package/dist/run/index.d.ts +1 -1
  119. package/dist/run/index.js +1 -1
  120. package/dist/run-credentials.d.ts +138 -0
  121. package/dist/run-credentials.js +152 -0
  122. package/dist/run-lifecycle.d.ts +92 -33
  123. package/dist/run-lifecycle.js +86 -73
  124. package/dist/scheduler.d.ts +8 -0
  125. package/dist/scheduler.js +2 -0
  126. package/dist/session-coordination.d.ts +115 -0
  127. package/dist/session-coordination.js +111 -0
  128. package/dist/store/agent-sessions/errors.d.ts +8 -0
  129. package/dist/store/agent-sessions/errors.js +10 -0
  130. package/dist/store/agent-sessions/index.d.ts +12 -0
  131. package/dist/store/agent-sessions/index.js +19 -0
  132. package/dist/store/agent-sessions/pglite.d.ts +12 -0
  133. package/dist/store/agent-sessions/pglite.js +7 -0
  134. package/dist/store/agent-sessions/ports.d.ts +24 -0
  135. package/dist/store/agent-sessions/ports.js +2 -0
  136. package/dist/store/agent-sessions/postgres.d.ts +12 -0
  137. package/dist/store/agent-sessions/postgres.js +7 -0
  138. package/dist/store/agent-sessions/schema.d.ts +3 -0
  139. package/dist/store/agent-sessions/schema.js +23 -0
  140. package/dist/store/agent-sessions/sql.d.ts +35 -0
  141. package/dist/store/agent-sessions/sql.js +59 -0
  142. package/dist/store/agent-sessions/sqlite.d.ts +117 -0
  143. package/dist/store/agent-sessions/sqlite.js +128 -0
  144. package/dist/store/event-log/sqlite.d.ts +75 -0
  145. package/dist/store/event-log/sqlite.js +230 -0
  146. package/dist/surface-exchanges.d.ts +97 -0
  147. package/dist/surface-exchanges.js +153 -0
  148. package/dist/terminal-session.d.ts +43 -11
  149. package/dist/terminal-session.js +22 -20
  150. package/dist/tokens.js +4 -4
  151. package/dist/tool-audit.d.ts +114 -0
  152. package/dist/tool-audit.js +95 -0
  153. package/dist/tool-executor.d.ts +22 -7
  154. package/dist/tool-executor.js +16 -16
  155. package/dist/tool-result-media.d.ts +3 -1
  156. package/dist/tool-result-media.js +1 -1
  157. package/dist/tool-result-surfaces.d.ts +3 -1
  158. package/dist/tool-result-surfaces.js +1 -1
  159. package/package.json +127 -9
@@ -23,22 +23,34 @@ export interface RoutineUpdateInput {
23
23
  /**
24
24
  * Storage-agnostic CRUD + read-only run-history port for routines. `@jini-ai/daemon` ships
25
25
  * {@link createInMemoryRoutineStore} as the reference implementation; a durable adapter
26
- * (`@jini-ai/sqlite`, future work) implements the same interface.
26
+ * (`@jini-ai/sqlite-chat`, future work) implements the same interface.
27
27
  */
28
28
  export interface RoutineStore {
29
29
  list(): Promise<readonly Routine[]>;
30
30
  /** Returns `null` when no routine with `id` exists — never throws for a missing id. */
31
- get(id: string): Promise<Routine | null>;
31
+ get(args: {
32
+ readonly id: string;
33
+ }): Promise<Routine | null>;
32
34
  /** Assigns and returns the new routine's `id` — never supplied by the caller, matching `EventLog.append`'s cursor-assignment precedent. */
33
- create(input: RoutineCreateInput): Promise<Routine>;
35
+ create(requiredArgs: Pick<RoutineCreateInput, "name" | "prompt" | "schedule" | "target">, optionalArgs?: Pick<RoutineCreateInput, "skillId" | "agentId" | "context" | "enabled">): Promise<Routine>;
34
36
  /** Applies a partial patch and returns the updated routine, or `null` if `id` does not exist. */
35
- update(id: string, patch: RoutineUpdateInput): Promise<Routine | null>;
37
+ update(args: {
38
+ readonly id: string;
39
+ readonly patch: RoutineUpdateInput;
40
+ }): Promise<Routine | null>;
36
41
  /** Returns whether a routine with `id` existed (and was removed). */
37
- delete(id: string): Promise<boolean>;
42
+ delete(args: {
43
+ readonly id: string;
44
+ }): Promise<boolean>;
38
45
  /** Returns up to `limit` runs for `routineId`, newest first. Returns `[]` for an unknown routine rather than throwing — mirrors `list()`'s empty-collection convention. */
39
- listRuns(routineId: string, limit: number): Promise<readonly RoutineRun[]>;
46
+ listRuns(args: {
47
+ readonly routineId: string;
48
+ readonly limit: number;
49
+ }): Promise<readonly RoutineRun[]>;
40
50
  /** Returns the most recently started run for `routineId`, or `null` if none has ever run. */
41
- getLatestRun(routineId: string): Promise<RoutineRun | null>;
51
+ getLatestRun(args: {
52
+ readonly routineId: string;
53
+ }): Promise<RoutineRun | null>;
42
54
  }
43
55
  /**
44
56
  * The display-shaped run summary OD's own `routineDbRowToContract` embedded on `Routine.lastRun`
@@ -47,7 +59,9 @@ export interface RoutineStore {
47
59
  * Kept as its own exported pure function (not inlined) so it is directly unit-testable, matching
48
60
  * this repo's "extract to a testable pure function" convention for awkward-to-cover branches.
49
61
  */
50
- export declare function summarizeLastRun(run: RoutineRun | null): Record<string, unknown> | null;
62
+ export declare function summarizeLastRun({ run }: {
63
+ readonly run: RoutineRun | null;
64
+ }): Record<string, unknown> | null;
51
65
  /**
52
66
  * `RoutineStore` plus two extra write hooks for run history that are deliberately NOT part of
53
67
  * the public {@link RoutineStore} contract (see this module's doc comment for why run-writing
@@ -59,19 +73,27 @@ export declare function summarizeLastRun(run: RoutineRun | null): Record<string,
59
73
  */
60
74
  export interface InMemoryRoutineStore extends RoutineStore {
61
75
  /** Records a run — mirrors `RoutinePersistence.insertRun`'s signature minus the scheduled-slot claim (this reference store has no concept of a "slot," only a flat run history), returning `false` if `run.id` was already recorded. */
62
- recordRun(run: RoutineRun): boolean;
76
+ recordRun(args: {
77
+ readonly run: RoutineRun;
78
+ }): boolean;
63
79
  /** Patches a previously recorded run in place. A patch for an unknown `id` is a no-op, mirroring `RoutinePersistence.updateRun`'s own silent-no-op-on-unknown-id convention (see `./scheduler.js`'s `updateRun` call sites, none of which check a return value). */
64
- patchRun(id: string, patch: Partial<RoutineRun>): void;
80
+ patchRun(args: {
81
+ readonly id: string;
82
+ readonly patch: Partial<RoutineRun>;
83
+ }): void;
65
84
  }
66
85
  /**
67
86
  * Reference `RoutineStore` implementation: an in-process `Map` of routines plus a flat array of
68
87
  * runs, no durable copy — matching `createInMemoryEventLog`'s own scope (a real persistent
69
- * adapter is `@jini-ai/sqlite`'s job).
88
+ * adapter is `@jini-ai/sqlite-chat`'s job).
70
89
  *
71
90
  * @complexity `get`/`create`/`update`/`delete` are O(1). `list` is O(n log n) (stable id-sort for
72
91
  * deterministic ordering). `listRuns`/`getLatestRun` are O(m log m) in the number of runs
73
92
  * recorded for the routine (m), since each sorts by `startedAt` descending on every call rather
74
93
  * than maintaining a secondary index — acceptable at this reference implementation's scale.
75
94
  */
76
- export declare function createInMemoryRoutineStore(): InMemoryRoutineStore;
95
+ export declare function createInMemoryRoutineStore(_requiredArgs: Record<string, never>, { now, newId }?: {
96
+ now?: () => number;
97
+ newId?: () => string;
98
+ }): InMemoryRoutineStore;
77
99
  //# sourceMappingURL=routine-store.d.ts.map
@@ -4,13 +4,13 @@
4
4
  * comment is the explicit template named by the porting proposal,
5
5
  * `ADS-memory/reports/proposals/PROP-http-route-packs-automation-routines-2026-07-21.md`): a
6
6
  * storage-agnostic interface plus an in-memory reference implementation, async-only from day one
7
- * (extraction-plan §2.6) so a durable `@jini-ai/sqlite` adapter is a drop-in swap later without an
7
+ * (extraction-plan §2.6) so a durable `@jini-ai/sqlite-chat` adapter is a drop-in swap later without an
8
8
  * API break.
9
9
  *
10
10
  * No such port existed anywhere in this repo before this file — OD's own `routine.ts` route file
11
11
  * called eight raw `db.js` SQL functions directly (`getRoutine`/`insertRoutine`/`updateRoutine`/
12
12
  * `deleteRoutine`/`listRoutines`/`listRoutineRuns`/`getLatestRoutineRun`/`getRoutineRun`) against
13
- * `routines`/`routine_runs` tables that `packages/sqlite/source-map.md` already documents as
13
+ * `routines`/`routine_runs` tables that `packages/sqlite/archived provenance ledger` already documents as
14
14
  * explicitly out of scope for the db-barrel port. This is the missing design.
15
15
  *
16
16
  * **Deliberately separate from `./scheduler.js`'s `RoutinePersistence`, not a shared interface**,
@@ -21,7 +21,7 @@
21
21
  * mirrors that same split: `RoutineStore` owns Routine CRUD and *read-only* run history
22
22
  * (`listRuns`/`getLatestRun`); it does not write run records — that stays the scheduler's job via
23
23
  * its own separately-injected `RoutinePersistence`. A host wiring both together against one real
24
- * durable table (a future `@jini-ai/sqlite` adapter) is host-level integration wiring, the same way
24
+ * durable table (a future `@jini-ai/sqlite-chat` adapter) is host-level integration wiring, the same way
25
25
  * `runs.ts`'s `onStarted` driver is host-supplied rather than built into `RunLifecycle` itself —
26
26
  * not attempted here, out of this port's scope.
27
27
  */
@@ -33,7 +33,7 @@ import { randomUUID } from 'node:crypto';
33
33
  * Kept as its own exported pure function (not inlined) so it is directly unit-testable, matching
34
34
  * this repo's "extract to a testable pure function" convention for awkward-to-cover branches.
35
35
  */
36
- export function summarizeLastRun(run) {
36
+ export function summarizeLastRun({ run }) {
37
37
  if (!run)
38
38
  return null;
39
39
  return {
@@ -70,14 +70,14 @@ function cloneRoutine(routine) {
70
70
  /**
71
71
  * Reference `RoutineStore` implementation: an in-process `Map` of routines plus a flat array of
72
72
  * runs, no durable copy — matching `createInMemoryEventLog`'s own scope (a real persistent
73
- * adapter is `@jini-ai/sqlite`'s job).
73
+ * adapter is `@jini-ai/sqlite-chat`'s job).
74
74
  *
75
75
  * @complexity `get`/`create`/`update`/`delete` are O(1). `list` is O(n log n) (stable id-sort for
76
76
  * deterministic ordering). `listRuns`/`getLatestRun` are O(m log m) in the number of runs
77
77
  * recorded for the routine (m), since each sorts by `startedAt` descending on every call rather
78
78
  * than maintaining a secondary index — acceptable at this reference implementation's scale.
79
79
  */
80
- export function createInMemoryRoutineStore() {
80
+ export function createInMemoryRoutineStore(_requiredArgs, { now = Date.now, newId = randomUUID } = {}) {
81
81
  const routines = new Map();
82
82
  const runs = [];
83
83
  function runsFor(routineId) {
@@ -87,21 +87,22 @@ export function createInMemoryRoutineStore() {
87
87
  }
88
88
  async function withLastRun(routine) {
89
89
  const latest = runsFor(routine.id)[0] ?? null;
90
- return { ...cloneRoutine(routine), lastRun: summarizeLastRun(latest) };
90
+ return { ...cloneRoutine(routine), lastRun: summarizeLastRun({ run: latest }) };
91
91
  }
92
92
  return {
93
93
  async list() {
94
94
  const ids = Array.from(routines.keys()).sort();
95
95
  return Promise.all(ids.map((id) => withLastRun(routines.get(id))));
96
96
  },
97
- async get(id) {
97
+ async get({ id }) {
98
98
  const routine = routines.get(id);
99
99
  return routine ? withLastRun(routine) : null;
100
100
  },
101
- async create(input) {
102
- const now = Date.now();
101
+ async create(requiredArgs, optionalArgs = {}) {
102
+ const input = { ...requiredArgs, ...optionalArgs };
103
+ const createdAt = now();
103
104
  const routine = {
104
- id: `routine-${randomUUID()}`,
105
+ id: `routine-${newId()}`,
105
106
  name: input.name,
106
107
  prompt: input.prompt,
107
108
  schedule: input.schedule,
@@ -112,13 +113,13 @@ export function createInMemoryRoutineStore() {
112
113
  enabled: input.enabled ?? true,
113
114
  nextRunAt: null,
114
115
  lastRun: null,
115
- createdAt: now,
116
- updatedAt: now,
116
+ createdAt: createdAt,
117
+ updatedAt: createdAt,
117
118
  };
118
119
  routines.set(routine.id, routine);
119
120
  return withLastRun(routine);
120
121
  },
121
- async update(id, patch) {
122
+ async update({ id, patch }) {
122
123
  const existing = routines.get(id);
123
124
  if (!existing)
124
125
  return null;
@@ -132,27 +133,27 @@ export function createInMemoryRoutineStore() {
132
133
  ...(patch.agentId !== undefined ? { agentId: patch.agentId } : {}),
133
134
  ...(patch.context !== undefined ? { context: patch.context } : {}),
134
135
  ...(patch.enabled !== undefined ? { enabled: patch.enabled } : {}),
135
- updatedAt: Date.now(),
136
+ updatedAt: now(),
136
137
  };
137
138
  routines.set(id, updated);
138
139
  return withLastRun(updated);
139
140
  },
140
- async delete(id) {
141
+ async delete({ id }) {
141
142
  return routines.delete(id);
142
143
  },
143
- async listRuns(routineId, limit) {
144
+ async listRuns({ routineId, limit }) {
144
145
  return runsFor(routineId).slice(0, limit);
145
146
  },
146
- async getLatestRun(routineId) {
147
+ async getLatestRun({ routineId }) {
147
148
  return runsFor(routineId)[0] ?? null;
148
149
  },
149
- recordRun(run) {
150
+ recordRun({ run }) {
150
151
  if (runs.some((existing) => existing.id === run.id))
151
152
  return false;
152
153
  runs.push({ ...run });
153
154
  return true;
154
155
  },
155
- patchRun(id, patch) {
156
+ patchRun({ id, patch }) {
156
157
  const run = runs.find((candidate) => candidate.id === id);
157
158
  if (run)
158
159
  Object.assign(run, patch);
@@ -18,7 +18,10 @@ import type { RoutineProjectTarget, RoutineSchedule, Weekday } from './types.js'
18
18
  * edge cases no timezone on this test runner's ICU data naturally triggers — directly testable
19
19
  * with a mocked `Intl.DateTimeFormat.prototype.formatToParts` instead of left unreachable.
20
20
  */
21
- export declare function partsInTimezone(timezone: string, atUtc: Date): {
21
+ export declare function partsInTimezone({ timezone, atUtc }: {
22
+ readonly timezone: string;
23
+ readonly atUtc: Date;
24
+ }): {
22
25
  year: number;
23
26
  month: number;
24
27
  day: number;
@@ -42,7 +45,14 @@ export declare function partsInTimezone(timezone: string, atUtc: Date): {
42
45
  * the unguarded `partsInTimezone` earlier in its own loop body, so an invalid timezone throws
43
46
  * there first — this function's defensive catch is otherwise unreachable through that call path.
44
47
  */
45
- export declare function tzWallToUtcCandidates(timezone: string, year: number, month: number, day: number, hour: number, minute: number): Date[];
48
+ export declare function tzWallToUtcCandidates({ timezone, year, month, day, hour, minute }: {
49
+ readonly timezone: string;
50
+ readonly year: number;
51
+ readonly month: number;
52
+ readonly day: number;
53
+ readonly hour: number;
54
+ readonly minute: number;
55
+ }): Date[];
46
56
  /**
47
57
  * Spring-forward gap fallback: when the requested wall time doesn't exist in `timezone` on this
48
58
  * day (clocks jumped over it), return the later of the two probe candidates. That instant has
@@ -53,12 +63,40 @@ export declare function tzWallToUtcCandidates(timezone: string, year: number, mo
53
63
  * Exported for the same reason as {@link tzWallToUtcCandidates} — its own `catch` branch is
54
64
  * otherwise unreachable through {@link nextWallTimeMatching}'s call path.
55
65
  */
56
- export declare function tzWallToUtcGapFallback(timezone: string, year: number, month: number, day: number, hour: number, minute: number): Date | null;
57
- export declare function nextHourlyRunAt(minute: number, now?: Date): Date;
58
- export declare function nextWallTimeMatching(timezone: string, time: string, predicate: (weekday: Weekday) => boolean, now: Date): Date | null;
59
- export declare function nextRunAtForSchedule(schedule: RoutineSchedule, now?: Date): Date | null;
60
- export declare function isValidWallTime(time: string): boolean;
61
- export declare function isValidTimezone(tz: string): boolean;
62
- export declare function validateSchedule(schedule: RoutineSchedule): void;
63
- export declare function validateTarget(target: RoutineProjectTarget): void;
66
+ export declare function tzWallToUtcGapFallback({ timezone, year, month, day, hour, minute }: {
67
+ readonly timezone: string;
68
+ readonly year: number;
69
+ readonly month: number;
70
+ readonly day: number;
71
+ readonly hour: number;
72
+ readonly minute: number;
73
+ }): Date | null;
74
+ export declare function nextHourlyRunAt({ minute }: {
75
+ readonly minute: number;
76
+ }, { now }?: {
77
+ readonly now?: Date;
78
+ }): Date;
79
+ export declare function nextWallTimeMatching({ timezone, time, predicate, now }: {
80
+ readonly timezone: string;
81
+ readonly time: string;
82
+ readonly predicate: (weekday: Weekday) => boolean;
83
+ readonly now: Date;
84
+ }): Date | null;
85
+ export declare function nextRunAtForSchedule({ schedule }: {
86
+ readonly schedule: RoutineSchedule;
87
+ }, { now }?: {
88
+ readonly now?: Date;
89
+ }): Date | null;
90
+ export declare function isValidWallTime({ time }: {
91
+ readonly time: string;
92
+ }): boolean;
93
+ export declare function isValidTimezone({ tz }: {
94
+ readonly tz: string;
95
+ }): boolean;
96
+ export declare function validateSchedule({ schedule }: {
97
+ readonly schedule: RoutineSchedule;
98
+ }): void;
99
+ export declare function validateTarget({ target }: {
100
+ readonly target: RoutineProjectTarget;
101
+ }): void;
64
102
  //# sourceMappingURL=schedule.d.ts.map
@@ -9,7 +9,7 @@
9
9
  * edge cases no timezone on this test runner's ICU data naturally triggers — directly testable
10
10
  * with a mocked `Intl.DateTimeFormat.prototype.formatToParts` instead of left unreachable.
11
11
  */
12
- export function partsInTimezone(timezone, atUtc) {
12
+ export function partsInTimezone({ timezone, atUtc }) {
13
13
  const dtf = new Intl.DateTimeFormat('en-US', {
14
14
  timeZone: timezone,
15
15
  hour12: false,
@@ -62,7 +62,7 @@ export function partsInTimezone(timezone, atUtc) {
62
62
  * the unguarded `partsInTimezone` earlier in its own loop body, so an invalid timezone throws
63
63
  * there first — this function's defensive catch is otherwise unreachable through that call path.
64
64
  */
65
- export function tzWallToUtcCandidates(timezone, year, month, day, hour, minute) {
65
+ export function tzWallToUtcCandidates({ timezone, year, month, day, hour, minute }) {
66
66
  try {
67
67
  const tentative = Date.UTC(year, month - 1, day, hour, minute, 0);
68
68
  const probeOffsetsMs = [-12, 0, 12].map((h) => h * 60 * 60_000);
@@ -95,7 +95,7 @@ export function tzWallToUtcCandidates(timezone, year, month, day, hour, minute)
95
95
  * Exported for the same reason as {@link tzWallToUtcCandidates} — its own `catch` branch is
96
96
  * otherwise unreachable through {@link nextWallTimeMatching}'s call path.
97
97
  */
98
- export function tzWallToUtcGapFallback(timezone, year, month, day, hour, minute) {
98
+ export function tzWallToUtcGapFallback({ timezone, year, month, day, hour, minute }) {
99
99
  try {
100
100
  const tentative = Date.UTC(year, month - 1, day, hour, minute, 0);
101
101
  const t1 = tzOffsetMinutes(timezone, new Date(tentative));
@@ -109,17 +109,17 @@ export function tzWallToUtcGapFallback(timezone, year, month, day, hour, minute)
109
109
  }
110
110
  }
111
111
  function matchesWallClock(timezone, at, year, month, day, hour, minute) {
112
- const p = partsInTimezone(timezone, at);
112
+ const p = partsInTimezone({ timezone: timezone, atUtc: at });
113
113
  return p.year === year && p.month === month && p.day === day && p.hour === hour && p.minute === minute;
114
114
  }
115
115
  /** Minutes east of UTC for `timezone` at instant `at`. e.g. Asia/Shanghai returns 480. */
116
116
  function tzOffsetMinutes(timezone, at) {
117
- const p = partsInTimezone(timezone, at);
117
+ const p = partsInTimezone({ timezone: timezone, atUtc: at });
118
118
  const asIfUtc = Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
119
119
  return Math.round((asIfUtc - at.getTime()) / 60_000);
120
120
  }
121
121
  // ---------- next-fire calculation ----------
122
- export function nextHourlyRunAt(minute, now = new Date()) {
122
+ export function nextHourlyRunAt({ minute }, { now = new Date() } = {}) {
123
123
  const next = new Date(now);
124
124
  next.setSeconds(0, 0);
125
125
  next.setMinutes(minute);
@@ -157,11 +157,11 @@ function parseWallTime(time) {
157
157
  * — see {@link nextWallTimeMatching} for when that actually happens.
158
158
  */
159
159
  function resolveDayFireInstant(timezone, date, hour, minute, now) {
160
- const candidates = tzWallToUtcCandidates(timezone, date.year, date.month, date.day, hour, minute);
160
+ const candidates = tzWallToUtcCandidates({ timezone: timezone, year: date.year, month: date.month, day: date.day, hour: hour, minute: minute });
161
161
  if (candidates.length === 0) {
162
162
  // Spring-forward gap: no valid wall instant exists today; pick the synthesized post-gap
163
163
  // fallback so the routine still fires today.
164
- const fallback = tzWallToUtcGapFallback(timezone, date.year, date.month, date.day, hour, minute);
164
+ const fallback = tzWallToUtcGapFallback({ timezone: timezone, year: date.year, month: date.month, day: date.day, hour: hour, minute: minute });
165
165
  if (!fallback)
166
166
  return { kind: 'abandon' };
167
167
  return fallback.getTime() > now.getTime() ? { kind: 'fire', at: fallback } : { kind: 'next-day' };
@@ -175,14 +175,14 @@ function resolveDayFireInstant(timezone, date, hour, minute, now) {
175
175
  }
176
176
  return { kind: 'next-day' };
177
177
  }
178
- export function nextWallTimeMatching(timezone, time, predicate, now) {
178
+ export function nextWallTimeMatching({ timezone, time, predicate, now }) {
179
179
  const wall = parseWallTime(time);
180
180
  if (!wall)
181
181
  return null;
182
182
  // Walk day by day in the target timezone.
183
183
  for (let offset = 0; offset < 14; offset += 1) {
184
184
  const probe = new Date(now.getTime() + offset * 24 * 60 * 60_000);
185
- const parts = partsInTimezone(timezone, probe);
185
+ const parts = partsInTimezone({ timezone: timezone, atUtc: probe });
186
186
  if (!predicate(parts.weekday))
187
187
  continue;
188
188
  const resolution = resolveDayFireInstant(timezone, parts, wall.hour, wall.minute, now);
@@ -202,24 +202,24 @@ export function nextWallTimeMatching(timezone, time, predicate, now) {
202
202
  }
203
203
  return null;
204
204
  }
205
- export function nextRunAtForSchedule(schedule, now = new Date()) {
205
+ export function nextRunAtForSchedule({ schedule }, { now = new Date() } = {}) {
206
206
  if (schedule.kind === 'hourly') {
207
- return nextHourlyRunAt(schedule.minute, now);
207
+ return nextHourlyRunAt({ minute: schedule.minute }, { now: now });
208
208
  }
209
209
  if (schedule.kind === 'daily') {
210
- return nextWallTimeMatching(schedule.timezone, schedule.time, () => true, now);
210
+ return nextWallTimeMatching({ timezone: schedule.timezone, time: schedule.time, predicate: () => true, now: now });
211
211
  }
212
212
  if (schedule.kind === 'weekdays') {
213
213
  // Mon=1 .. Fri=5
214
- return nextWallTimeMatching(schedule.timezone, schedule.time, (w) => w >= 1 && w <= 5, now);
214
+ return nextWallTimeMatching({ timezone: schedule.timezone, time: schedule.time, predicate: (w) => w >= 1 && w <= 5, now: now });
215
215
  }
216
216
  if (schedule.kind === 'weekly') {
217
- return nextWallTimeMatching(schedule.timezone, schedule.time, (w) => w === schedule.weekday, now);
217
+ return nextWallTimeMatching({ timezone: schedule.timezone, time: schedule.time, predicate: (w) => w === schedule.weekday, now: now });
218
218
  }
219
219
  return null;
220
220
  }
221
221
  // ---------- validation ----------
222
- export function isValidWallTime(time) {
222
+ export function isValidWallTime({ time }) {
223
223
  const m = /^(\d{2}):(\d{2})$/.exec(time);
224
224
  if (!m)
225
225
  return false;
@@ -227,7 +227,7 @@ export function isValidWallTime(time) {
227
227
  const mm = Number(m[2]);
228
228
  return h >= 0 && h <= 23 && mm >= 0 && mm <= 59;
229
229
  }
230
- export function isValidTimezone(tz) {
230
+ export function isValidTimezone({ tz }) {
231
231
  if (!tz || typeof tz !== 'string')
232
232
  return false;
233
233
  try {
@@ -249,17 +249,17 @@ function validateWeekday(weekday) {
249
249
  }
250
250
  }
251
251
  function validateWallClockSchedule(schedule) {
252
- if (!isValidWallTime(schedule.time)) {
252
+ if (!isValidWallTime({ time: schedule.time })) {
253
253
  throw new Error(`Invalid time: ${schedule.time}`);
254
254
  }
255
- if (!isValidTimezone(schedule.timezone)) {
255
+ if (!isValidTimezone({ tz: schedule.timezone })) {
256
256
  throw new Error(`Invalid timezone: ${schedule.timezone}`);
257
257
  }
258
258
  if (schedule.kind === 'weekly') {
259
259
  validateWeekday(schedule.weekday);
260
260
  }
261
261
  }
262
- export function validateSchedule(schedule) {
262
+ export function validateSchedule({ schedule }) {
263
263
  if (!schedule || typeof schedule !== 'object') {
264
264
  throw new Error('schedule is required');
265
265
  }
@@ -273,7 +273,7 @@ export function validateSchedule(schedule) {
273
273
  }
274
274
  throw new Error(`Unsupported schedule kind: ${schedule.kind}`);
275
275
  }
276
- export function validateTarget(target) {
276
+ export function validateTarget({ target }) {
277
277
  if (!target || typeof target !== 'object') {
278
278
  throw new Error('target is required');
279
279
  }
@@ -9,26 +9,47 @@ export declare class ScheduledRunPersistenceError extends Error {
9
9
  readonly routineId: string;
10
10
  readonly slotAt: number;
11
11
  readonly originalError: unknown;
12
- constructor(routineId: string, slotAt: number, originalError: unknown);
12
+ constructor({ routineId, slotAt, originalError }: {
13
+ readonly routineId: string;
14
+ readonly slotAt: number;
15
+ readonly originalError: unknown;
16
+ });
13
17
  }
14
18
  export declare class RoutineService {
15
- private readonly persistence;
16
19
  private timers;
17
20
  private inflight;
18
21
  private runHandler;
19
22
  private started;
20
- constructor(persistence: RoutinePersistence);
21
- setRunHandler(handler: RoutineRunHandler): void;
22
- start(): void;
23
- stop(): void;
24
- rescheduleAll(): void;
25
- rescheduleOne(routineId: string): void;
26
- unschedule(routineId: string): void;
23
+ private readonly persistence;
24
+ private readonly now;
25
+ private readonly newId;
26
+ constructor({ persistence }: {
27
+ readonly persistence: RoutinePersistence;
28
+ }, { now, newId }?: {
29
+ now?: () => number;
30
+ newId?: () => string;
31
+ });
32
+ setRunHandler({ handler }: {
33
+ readonly handler: RoutineRunHandler;
34
+ }): void;
35
+ start(_args: Record<string, never>): void;
36
+ stop(_args: Record<string, never>): void;
37
+ rescheduleAll(_args: Record<string, never>): void;
38
+ rescheduleOne({ routineId }: {
39
+ readonly routineId: string;
40
+ }): void;
41
+ unschedule({ routineId }: {
42
+ readonly routineId: string;
43
+ }): void;
27
44
  private scheduleRoutine;
28
45
  private retryScheduledSlot;
29
46
  private scheduleRoutineAt;
30
- nextRunAt(routineId: string): Date | null;
31
- runNow(routineId: string): Promise<RoutineRunHandlerStart>;
47
+ nextRunAt({ routineId }: {
48
+ readonly routineId: string;
49
+ }): Date | null;
50
+ runNow({ routineId }: {
51
+ readonly routineId: string;
52
+ }): Promise<RoutineRunHandlerStart>;
32
53
  /**
33
54
  * Wraps a persistence failure as a {@link ScheduledRunPersistenceError} when this attempt was a
34
55
  * scheduled fire, so the caller retries the same slot instead of advancing the cadence. Manual