@jini-ai/daemon 0.1.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 (142) hide show
  1. package/LICENSE +201 -0
  2. package/dist/agent-executor.d.ts +458 -0
  3. package/dist/agent-executor.d.ts.map +1 -0
  4. package/dist/agent-executor.js +1277 -0
  5. package/dist/agent-executor.js.map +1 -0
  6. package/dist/artifacts/index.d.ts +14 -0
  7. package/dist/artifacts/index.d.ts.map +1 -0
  8. package/dist/artifacts/index.js +14 -0
  9. package/dist/artifacts/index.js.map +1 -0
  10. package/dist/artifacts/manifest.d.ts +90 -0
  11. package/dist/artifacts/manifest.d.ts.map +1 -0
  12. package/dist/artifacts/manifest.js +229 -0
  13. package/dist/artifacts/manifest.js.map +1 -0
  14. package/dist/artifacts/publication-guard.d.ts +28 -0
  15. package/dist/artifacts/publication-guard.d.ts.map +1 -0
  16. package/dist/artifacts/publication-guard.js +77 -0
  17. package/dist/artifacts/publication-guard.js.map +1 -0
  18. package/dist/artifacts/runtime-compat.d.ts +37 -0
  19. package/dist/artifacts/runtime-compat.d.ts.map +1 -0
  20. package/dist/artifacts/runtime-compat.js +33 -0
  21. package/dist/artifacts/runtime-compat.js.map +1 -0
  22. package/dist/artifacts/store.d.ts +85 -0
  23. package/dist/artifacts/store.d.ts.map +1 -0
  24. package/dist/artifacts/store.js +92 -0
  25. package/dist/artifacts/store.js.map +1 -0
  26. package/dist/artifacts/stub-guard.d.ts +73 -0
  27. package/dist/artifacts/stub-guard.d.ts.map +1 -0
  28. package/dist/artifacts/stub-guard.js +198 -0
  29. package/dist/artifacts/stub-guard.js.map +1 -0
  30. package/dist/artifacts/text-suppression.d.ts +68 -0
  31. package/dist/artifacts/text-suppression.d.ts.map +1 -0
  32. package/dist/artifacts/text-suppression.js +184 -0
  33. package/dist/artifacts/text-suppression.js.map +1 -0
  34. package/dist/close-status.d.ts +86 -0
  35. package/dist/close-status.d.ts.map +1 -0
  36. package/dist/close-status.js +91 -0
  37. package/dist/close-status.js.map +1 -0
  38. package/dist/continuation/continuation-transport.d.ts +41 -0
  39. package/dist/continuation/continuation-transport.d.ts.map +1 -0
  40. package/dist/continuation/continuation-transport.js +18 -0
  41. package/dist/continuation/continuation-transport.js.map +1 -0
  42. package/dist/continuation/index.d.ts +4 -0
  43. package/dist/continuation/index.d.ts.map +1 -0
  44. package/dist/continuation/index.js +4 -0
  45. package/dist/continuation/index.js.map +1 -0
  46. package/dist/continuation/journal.d.ts +37 -0
  47. package/dist/continuation/journal.d.ts.map +1 -0
  48. package/dist/continuation/journal.js +23 -0
  49. package/dist/continuation/journal.js.map +1 -0
  50. package/dist/continuation/run-start-handler.d.ts +75 -0
  51. package/dist/continuation/run-start-handler.d.ts.map +1 -0
  52. package/dist/continuation/run-start-handler.js +32 -0
  53. package/dist/continuation/run-start-handler.js.map +1 -0
  54. package/dist/delegated-tool-bridge.d.ts +53 -0
  55. package/dist/delegated-tool-bridge.d.ts.map +1 -0
  56. package/dist/delegated-tool-bridge.js +92 -0
  57. package/dist/delegated-tool-bridge.js.map +1 -0
  58. package/dist/event-log.d.ts +134 -0
  59. package/dist/event-log.d.ts.map +1 -0
  60. package/dist/event-log.js +108 -0
  61. package/dist/event-log.js.map +1 -0
  62. package/dist/frontend-capability-tools.d.ts +102 -0
  63. package/dist/frontend-capability-tools.d.ts.map +1 -0
  64. package/dist/frontend-capability-tools.js +68 -0
  65. package/dist/frontend-capability-tools.js.map +1 -0
  66. package/dist/frontend-session-registry.d.ts +127 -0
  67. package/dist/frontend-session-registry.d.ts.map +1 -0
  68. package/dist/frontend-session-registry.js +180 -0
  69. package/dist/frontend-session-registry.js.map +1 -0
  70. package/dist/index.d.ts +38 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +38 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/legacy-data-migration.d.ts +104 -0
  75. package/dist/legacy-data-migration.d.ts.map +1 -0
  76. package/dist/legacy-data-migration.js +239 -0
  77. package/dist/legacy-data-migration.js.map +1 -0
  78. package/dist/routines/index.d.ts +12 -0
  79. package/dist/routines/index.d.ts.map +1 -0
  80. package/dist/routines/index.js +12 -0
  81. package/dist/routines/index.js.map +1 -0
  82. package/dist/routines/routine-store.d.ts +77 -0
  83. package/dist/routines/routine-store.d.ts.map +1 -0
  84. package/dist/routines/routine-store.js +162 -0
  85. package/dist/routines/routine-store.js.map +1 -0
  86. package/dist/routines/schedule.d.ts +72 -0
  87. package/dist/routines/schedule.d.ts.map +1 -0
  88. package/dist/routines/schedule.js +263 -0
  89. package/dist/routines/schedule.js.map +1 -0
  90. package/dist/routines/scheduler.d.ts +34 -0
  91. package/dist/routines/scheduler.d.ts.map +1 -0
  92. package/dist/routines/scheduler.js +330 -0
  93. package/dist/routines/scheduler.js.map +1 -0
  94. package/dist/routines/types.d.ts +131 -0
  95. package/dist/routines/types.d.ts.map +1 -0
  96. package/dist/routines/types.js +9 -0
  97. package/dist/routines/types.js.map +1 -0
  98. package/dist/run/core/failure-taxonomy.d.ts +27 -0
  99. package/dist/run/core/failure-taxonomy.d.ts.map +1 -0
  100. package/dist/run/core/failure-taxonomy.js +16 -0
  101. package/dist/run/core/failure-taxonomy.js.map +1 -0
  102. package/dist/run/core/index.d.ts +5 -0
  103. package/dist/run/core/index.d.ts.map +1 -0
  104. package/dist/run/core/index.js +5 -0
  105. package/dist/run/core/index.js.map +1 -0
  106. package/dist/run/core/result.d.ts +25 -0
  107. package/dist/run/core/result.d.ts.map +1 -0
  108. package/dist/run/core/result.js +52 -0
  109. package/dist/run/core/result.js.map +1 -0
  110. package/dist/run/core/retry.d.ts +106 -0
  111. package/dist/run/core/retry.d.ts.map +1 -0
  112. package/dist/run/core/retry.js +210 -0
  113. package/dist/run/core/retry.js.map +1 -0
  114. package/dist/run/diagnostics/diagnostics.d.ts +86 -0
  115. package/dist/run/diagnostics/diagnostics.d.ts.map +1 -0
  116. package/dist/run/diagnostics/diagnostics.js +211 -0
  117. package/dist/run/diagnostics/diagnostics.js.map +1 -0
  118. package/dist/run/diagnostics/index.d.ts +3 -0
  119. package/dist/run/diagnostics/index.d.ts.map +1 -0
  120. package/dist/run/diagnostics/index.js +3 -0
  121. package/dist/run/diagnostics/index.js.map +1 -0
  122. package/dist/run/index.d.ts +16 -0
  123. package/dist/run/index.d.ts.map +1 -0
  124. package/dist/run/index.js +16 -0
  125. package/dist/run/index.js.map +1 -0
  126. package/dist/run-lifecycle.d.ts +118 -0
  127. package/dist/run-lifecycle.d.ts.map +1 -0
  128. package/dist/run-lifecycle.js +451 -0
  129. package/dist/run-lifecycle.js.map +1 -0
  130. package/dist/terminal-session.d.ts +139 -0
  131. package/dist/terminal-session.d.ts.map +1 -0
  132. package/dist/terminal-session.js +316 -0
  133. package/dist/terminal-session.js.map +1 -0
  134. package/dist/tokens.d.ts +9 -0
  135. package/dist/tokens.d.ts.map +1 -0
  136. package/dist/tokens.js +19 -0
  137. package/dist/tokens.js.map +1 -0
  138. package/dist/tool-executor.d.ts +97 -0
  139. package/dist/tool-executor.d.ts.map +1 -0
  140. package/dist/tool-executor.js +198 -0
  141. package/dist/tool-executor.js.map +1 -0
  142. package/package.json +51 -0
@@ -0,0 +1,239 @@
1
+ /**
2
+ * @module legacy-data-migration
3
+ *
4
+ * A one-shot, idempotent, host-configured migrator for moving a daemon's
5
+ * data root from one location to another (e.g. a pre-packaging install that
6
+ * ran out of a repo checkout, moved to a per-namespace directory under an
7
+ * OS user-data location). The mechanism — refuse-if-missing-proof,
8
+ * refuse-if-destination-populated, refuse-if-already-migrated, reject
9
+ * symlinks, stage-then-atomically-promote, rollback-on-any-failure — is
10
+ * product-neutral; the *shape* of a data root's payload (which files/dirs
11
+ * constitute "real data", and which single entry proves a directory holds
12
+ * real payload rather than an empty scaffold) is host-supplied via
13
+ * {@link LegacyDataMigrationConfig}, never hardcoded here.
14
+ *
15
+ * Sync by design: intended to run at daemon startup before any database
16
+ * connection opens, so it can't race a concurrent open of the destination.
17
+ */
18
+ import * as fs from 'node:fs';
19
+ import * as path from 'node:path';
20
+ const DEFAULT_MARKER_FILE = '.migrated-from';
21
+ /**
22
+ * Thrown when `legacyDir` is explicitly set but is not a usable legacy data
23
+ * dir, or the new `dataDir` is already populated and would be merged into.
24
+ * Failing loud here is the point: a silent skip trains an operator to
25
+ * assume a migration ran when it didn't.
26
+ */
27
+ export class LegacyMigrationError extends Error {
28
+ code;
29
+ constructor(code, message) {
30
+ super(message);
31
+ this.code = code;
32
+ this.name = 'LegacyMigrationError';
33
+ }
34
+ }
35
+ function isExistingDir(p) {
36
+ try {
37
+ return fs.statSync(p).isDirectory();
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ function isExistingFile(p) {
44
+ try {
45
+ return fs.statSync(p).isFile();
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ /**
52
+ * Returns true when `dataDir` looks like a fresh / never-used data root:
53
+ * either the directory does not exist, or it exists but does not contain
54
+ * the configured `proofEntry` as a file. Deliberately not just "the
55
+ * directory is empty" — a packaged install commonly pre-creates an empty
56
+ * `dataDir` before the daemon ever boots, so emptiness alone is not proof
57
+ * of "no prior data."
58
+ *
59
+ * @param dataDir - The data root to check.
60
+ * @param config - The host's payload/proof-entry configuration.
61
+ */
62
+ export function dataDirIsEmptyOrFresh(dataDir, config) {
63
+ if (!isExistingDir(dataDir))
64
+ return true;
65
+ return !isExistingFile(path.join(dataDir, config.proofEntry));
66
+ }
67
+ /**
68
+ * Returns true when `legacyDir` contains a payload worth migrating: it
69
+ * exists and its configured `proofEntry` exists as a file.
70
+ *
71
+ * @param legacyDir - The candidate legacy data directory.
72
+ * @param config - The host's payload/proof-entry configuration.
73
+ */
74
+ export function legacyDirHasPayload(legacyDir, config) {
75
+ if (!isExistingDir(legacyDir))
76
+ return false;
77
+ return isExistingFile(path.join(legacyDir, config.proofEntry));
78
+ }
79
+ /**
80
+ * Returns the list of configured payload entries that already exist under
81
+ * `dataDir`. Migration refuses to merge on top of any of them — a database
82
+ * file/WAL pair or a project tree cannot be safely interleaved with a
83
+ * foreign counterpart, and a partial overlay is worse than no migration.
84
+ *
85
+ * @param dataDir - The data root to check.
86
+ * @param config - The host's payload/proof-entry configuration.
87
+ */
88
+ export function dataDirHasExistingPayload(dataDir, config) {
89
+ if (!isExistingDir(dataDir))
90
+ return [];
91
+ return config.payloadEntries.filter((entry) => fs.existsSync(path.join(dataDir, entry)));
92
+ }
93
+ /**
94
+ * Walk a payload subtree and refuse to copy if any node is a symlink — a
95
+ * preserved symlink would let a downstream reader escape the data root.
96
+ * @internal
97
+ */
98
+ function assertNoSymlinks(srcRoot, displayPath = srcRoot) {
99
+ const stat = fs.lstatSync(srcRoot);
100
+ if (stat.isSymbolicLink()) {
101
+ throw new LegacyMigrationError('symlink_in_payload', `legacy payload contains a symlink at "${displayPath}"; refusing to migrate`);
102
+ }
103
+ if (!stat.isDirectory())
104
+ return;
105
+ for (const child of fs.readdirSync(srcRoot)) {
106
+ assertNoSymlinks(path.join(srcRoot, child), path.join(displayPath, child));
107
+ }
108
+ }
109
+ /** Stage every present payload entry into `stagingDir`. Returns the entries actually copied. @internal */
110
+ function stagePayload(legacyDir, stagingDir, payloadEntries) {
111
+ fs.mkdirSync(stagingDir, { recursive: true });
112
+ const copied = [];
113
+ for (const entry of payloadEntries) {
114
+ const src = path.join(legacyDir, entry);
115
+ if (!fs.existsSync(src))
116
+ continue;
117
+ assertNoSymlinks(src);
118
+ const dst = path.join(stagingDir, entry);
119
+ fs.cpSync(src, dst, { recursive: true, force: true, errorOnExist: false });
120
+ copied.push(entry);
121
+ }
122
+ return copied;
123
+ }
124
+ /**
125
+ * Move staged payload entries into the final `dataDir`. Each entry is moved
126
+ * with `renameSync` (atomic on the same filesystem); a cross-device rename
127
+ * falls back to copy + remove. Rollback-safe in two layers: the copy
128
+ * fallback removes a partial destination on its own failure, and the outer
129
+ * loop removes every entry it already promoted before rethrowing.
130
+ *
131
+ * @param stagingDir - The sibling staging directory holding the copied payload.
132
+ * @param dataDir - The destination data root.
133
+ * @param entries - The payload entries to promote.
134
+ * @returns The entries that were placed into `dataDir` before any failure.
135
+ */
136
+ export function promoteStaged(stagingDir, dataDir, entries) {
137
+ fs.mkdirSync(dataDir, { recursive: true });
138
+ const promoted = [];
139
+ try {
140
+ for (const entry of entries) {
141
+ const src = path.join(stagingDir, entry);
142
+ const dst = path.join(dataDir, entry);
143
+ try {
144
+ fs.renameSync(src, dst);
145
+ }
146
+ catch {
147
+ try {
148
+ fs.cpSync(src, dst, { recursive: true, force: true, errorOnExist: false });
149
+ fs.rmSync(src, { recursive: true, force: true });
150
+ }
151
+ catch (cpErr) {
152
+ fs.rmSync(dst, { recursive: true, force: true });
153
+ throw cpErr;
154
+ }
155
+ }
156
+ promoted.push(entry);
157
+ }
158
+ }
159
+ catch (err) {
160
+ rollbackPromoted(dataDir, promoted);
161
+ throw err;
162
+ }
163
+ return promoted;
164
+ }
165
+ function rollbackPromoted(dataDir, promoted) {
166
+ for (const entry of promoted) {
167
+ fs.rmSync(path.join(dataDir, entry), { recursive: true, force: true });
168
+ }
169
+ }
170
+ function writeMarkerDefault(dataDir, legacyDir, markerFile) {
171
+ const marker = path.join(dataDir, markerFile);
172
+ const temp = `${marker}.tmp-${process.pid}-${Date.now()}`;
173
+ const payload = JSON.stringify({ legacyDir: path.resolve(legacyDir), migratedAt: new Date().toISOString() }, null, 2);
174
+ try {
175
+ fs.writeFileSync(temp, `${payload}\n`, 'utf8');
176
+ fs.renameSync(temp, marker);
177
+ }
178
+ catch (err) {
179
+ fs.rmSync(temp, { force: true });
180
+ throw err;
181
+ }
182
+ }
183
+ /**
184
+ * One-shot, idempotent legacy data migrator. Synchronous so it can run at
185
+ * module import time before a database connection opens. Throws
186
+ * {@link LegacyMigrationError} on operator-actionable failures (env set but
187
+ * path invalid; `dataDir` already populated; symlink in payload).
188
+ *
189
+ * @param options - Legacy/current dir paths plus the host's payload config.
190
+ * @returns The migration outcome (`noop` when unconfigured, `skipped` when
191
+ * already migrated, `migrated` with the copied entry list otherwise).
192
+ */
193
+ export function migrateLegacyDataDirSync(options) {
194
+ const log = options.logger ?? { info: (m) => console.log(`[jini-migrate] ${m}`) };
195
+ const markerFile = options.markerFile ?? DEFAULT_MARKER_FILE;
196
+ const raw = options.legacyDir;
197
+ if (raw === undefined || raw.length === 0) {
198
+ return { status: 'noop', reason: 'legacy data dir not configured' };
199
+ }
200
+ const legacyDir = path.resolve(raw);
201
+ const dataDir = path.resolve(options.dataDir);
202
+ if (legacyDir === dataDir) {
203
+ return { status: 'noop', reason: 'legacy data dir equals current data dir' };
204
+ }
205
+ // Marker check runs before legacyDirHasPayload on purpose: once a
206
+ // migration has succeeded, the marker is the canonical "do not touch"
207
+ // signal, even if the legacy source is later moved or deleted.
208
+ const markerPath = path.join(dataDir, markerFile);
209
+ if (fs.existsSync(markerPath)) {
210
+ return { status: 'skipped', reason: 'migration marker already present' };
211
+ }
212
+ if (!legacyDirHasPayload(legacyDir, options)) {
213
+ throw new LegacyMigrationError('legacy_dir_invalid', `legacy data dir "${legacyDir}" is not a usable legacy data dir (expected "${options.proofEntry}" directly inside it).`);
214
+ }
215
+ const existing = dataDirHasExistingPayload(dataDir, options);
216
+ if (existing.length > 0) {
217
+ throw new LegacyMigrationError('data_dir_not_empty', `data dir "${dataDir}" already contains payload entries (${existing.join(', ')}); refusing to merge legacy data on top. Move the existing data aside or pick a fresh data root before retrying.`);
218
+ }
219
+ log.info(`migrating legacy data from "${legacyDir}" to "${dataDir}"`);
220
+ fs.mkdirSync(path.dirname(dataDir), { recursive: true });
221
+ const stagingDir = path.join(path.dirname(dataDir), `${path.basename(dataDir)}.migrate-${process.pid}-${Date.now()}`);
222
+ let copied;
223
+ let promoted = [];
224
+ try {
225
+ copied = stagePayload(legacyDir, stagingDir, options.payloadEntries);
226
+ promoted = promoteStaged(stagingDir, dataDir, copied);
227
+ (options.writeMarker ?? writeMarkerDefault)(dataDir, legacyDir, markerFile);
228
+ }
229
+ catch (err) {
230
+ rollbackPromoted(dataDir, promoted);
231
+ fs.rmSync(markerPath, { force: true });
232
+ fs.rmSync(stagingDir, { recursive: true, force: true });
233
+ throw err;
234
+ }
235
+ fs.rmSync(stagingDir, { recursive: true, force: true });
236
+ log.info(`migration complete: copied ${copied.length} entr${copied.length === 1 ? 'y' : 'ies'} (${copied.join(', ')})`);
237
+ return { status: 'migrated', reason: 'copied legacy payload', copied };
238
+ }
239
+ //# sourceMappingURL=legacy-data-migration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy-data-migration.js","sourceRoot":"","sources":["../src/legacy-data-migration.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @module routines
3
+ *
4
+ * The routine scheduler ({@link ./scheduler.js}'s `RoutineService`, `./schedule.js`'s DST-safe
5
+ * wall-clock math) plus its HTTP-facing CRUD + run-history counterpart ({@link ./routine-store.js}'s
6
+ * `RoutineStore`). See `../../source-map.md`'s dated routines section for full provenance.
7
+ */
8
+ export * from './types.js';
9
+ export * from './schedule.js';
10
+ export * from './scheduler.js';
11
+ export * from './routine-store.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routines/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @module routines
3
+ *
4
+ * The routine scheduler ({@link ./scheduler.js}'s `RoutineService`, `./schedule.js`'s DST-safe
5
+ * wall-clock math) plus its HTTP-facing CRUD + run-history counterpart ({@link ./routine-store.js}'s
6
+ * `RoutineStore`). See `../../source-map.md`'s dated routines section for full provenance.
7
+ */
8
+ export * from './types.js';
9
+ export * from './schedule.js';
10
+ export * from './scheduler.js';
11
+ export * from './routine-store.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routines/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,77 @@
1
+ import type { Routine, RoutineContextSelection, RoutineProjectTarget, RoutineRun, RoutineSchedule } from './types.js';
2
+ export interface RoutineCreateInput {
3
+ readonly name: string;
4
+ readonly prompt: string;
5
+ readonly schedule: RoutineSchedule;
6
+ readonly target: RoutineProjectTarget;
7
+ readonly skillId?: string | null;
8
+ readonly agentId?: string | null;
9
+ readonly context?: RoutineContextSelection;
10
+ /** @default true */
11
+ readonly enabled?: boolean;
12
+ }
13
+ export interface RoutineUpdateInput {
14
+ readonly name?: string;
15
+ readonly prompt?: string;
16
+ readonly schedule?: RoutineSchedule;
17
+ readonly target?: RoutineProjectTarget;
18
+ readonly skillId?: string | null;
19
+ readonly agentId?: string | null;
20
+ readonly context?: RoutineContextSelection;
21
+ readonly enabled?: boolean;
22
+ }
23
+ /**
24
+ * Storage-agnostic CRUD + read-only run-history port for routines. `@jini-ai/daemon` ships
25
+ * {@link createInMemoryRoutineStore} as the reference implementation; a durable adapter
26
+ * (`@jini-ai/sqlite`, future work) implements the same interface.
27
+ */
28
+ export interface RoutineStore {
29
+ list(): Promise<readonly Routine[]>;
30
+ /** Returns `null` when no routine with `id` exists — never throws for a missing id. */
31
+ get(id: string): Promise<Routine | null>;
32
+ /** 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>;
34
+ /** 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>;
36
+ /** Returns whether a routine with `id` existed (and was removed). */
37
+ delete(id: string): Promise<boolean>;
38
+ /** 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[]>;
40
+ /** Returns the most recently started run for `routineId`, or `null` if none has ever run. */
41
+ getLatestRun(routineId: string): Promise<RoutineRun | null>;
42
+ }
43
+ /**
44
+ * The display-shaped run summary OD's own `routineDbRowToContract` embedded on `Routine.lastRun`
45
+ * — a filtered/relabeled projection of a `RoutineRun` (its `id` renamed to `runId`; nullish
46
+ * `completedAt`/`summary`/`error`/`errorCode` omitted entirely rather than included as `null`).
47
+ * Kept as its own exported pure function (not inlined) so it is directly unit-testable, matching
48
+ * this repo's "extract to a testable pure function" convention for awkward-to-cover branches.
49
+ */
50
+ export declare function summarizeLastRun(run: RoutineRun | null): Record<string, unknown> | null;
51
+ /**
52
+ * `RoutineStore` plus two extra write hooks for run history that are deliberately NOT part of
53
+ * the public {@link RoutineStore} contract (see this module's doc comment for why run-writing
54
+ * stays out of the interface). Exposed as a concrete-type superset so:
55
+ * 1. This module's own tests can seed `listRuns`/`getLatestRun` fixtures without a second store.
56
+ * 2. A host bridging `./scheduler.js`'s `RoutinePersistence.insertRun`/`updateRun` calls into
57
+ * this same in-memory instance (so `RoutineStore`'s read side observes the scheduler's writes)
58
+ * has a documented, shape-matched target to forward them to.
59
+ */
60
+ export interface InMemoryRoutineStore extends RoutineStore {
61
+ /** 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;
63
+ /** 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;
65
+ }
66
+ /**
67
+ * Reference `RoutineStore` implementation: an in-process `Map` of routines plus a flat array of
68
+ * runs, no durable copy — matching `createInMemoryEventLog`'s own scope (a real persistent
69
+ * adapter is `@jini-ai/sqlite`'s job).
70
+ *
71
+ * @complexity `get`/`create`/`update`/`delete` are O(1). `list` is O(n log n) (stable id-sort for
72
+ * deterministic ordering). `listRuns`/`getLatestRun` are O(m log m) in the number of runs
73
+ * recorded for the routine (m), since each sorts by `startedAt` descending on every call rather
74
+ * than maintaining a secondary index — acceptable at this reference implementation's scale.
75
+ */
76
+ export declare function createInMemoryRoutineStore(): InMemoryRoutineStore;
77
+ //# sourceMappingURL=routine-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routine-store.d.ts","sourceRoot":"","sources":["../../src/routines/routine-store.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEtH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,uBAAuB,CAAC;IAC3C,oBAAoB;IACpB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,oBAAoB,CAAC;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,uBAAuB,CAAC;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,IAAI,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC;IACpC,uFAAuF;IACvF,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACzC,2IAA2I;IAC3I,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,iGAAiG;IACjG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACvE,qEAAqE;IACrE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrC,2KAA2K;IAC3K,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;IAC3E,6FAA6F;IAC7F,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;CAC7D;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAevF;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACxD,wOAAwO;IACxO,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC;IACpC,oQAAoQ;IACpQ,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;CACxD;AAqBD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,IAAI,oBAAoB,CAyFjE"}
@@ -0,0 +1,162 @@
1
+ /**
2
+ * `RoutineStore` — the CRUD + run-history persistence port for `@jini-ai/http`'s routine routes.
3
+ * Designed the same way `../event-log.js`'s `EventLog` is designed (that module's own doc
4
+ * comment is the explicit template named by the porting proposal,
5
+ * `ADS-memory/reports/proposals/PROP-http-route-packs-automation-routines-2026-07-21.md`): a
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
8
+ * API break.
9
+ *
10
+ * No such port existed anywhere in this repo before this file — OD's own `routine.ts` route file
11
+ * called eight raw `db.js` SQL functions directly (`getRoutine`/`insertRoutine`/`updateRoutine`/
12
+ * `deleteRoutine`/`listRoutines`/`listRoutineRuns`/`getLatestRoutineRun`/`getRoutineRun`) against
13
+ * `routines`/`routine_runs` tables that `packages/sqlite/source-map.md` already documents as
14
+ * explicitly out of scope for the db-barrel port. This is the missing design.
15
+ *
16
+ * **Deliberately separate from `./scheduler.js`'s `RoutinePersistence`, not a shared interface**,
17
+ * matching OD's own architecture: OD itself used two different narrow ports over the same
18
+ * conceptual `routine_runs` table — `RoutinePersistence.insertRun`/`updateRun` (injected into
19
+ * `RoutineService`, the scheduler's write path for a routine actually firing) and `routine.ts`'s
20
+ * own direct DB reads (the HTTP layer's read path for CRUD + run-history display). This port
21
+ * mirrors that same split: `RoutineStore` owns Routine CRUD and *read-only* run history
22
+ * (`listRuns`/`getLatestRun`); it does not write run records — that stays the scheduler's job via
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
25
+ * `runs.ts`'s `onStarted` driver is host-supplied rather than built into `RunLifecycle` itself —
26
+ * not attempted here, out of this port's scope.
27
+ */
28
+ import { randomUUID } from 'node:crypto';
29
+ /**
30
+ * The display-shaped run summary OD's own `routineDbRowToContract` embedded on `Routine.lastRun`
31
+ * — a filtered/relabeled projection of a `RoutineRun` (its `id` renamed to `runId`; nullish
32
+ * `completedAt`/`summary`/`error`/`errorCode` omitted entirely rather than included as `null`).
33
+ * Kept as its own exported pure function (not inlined) so it is directly unit-testable, matching
34
+ * this repo's "extract to a testable pure function" convention for awkward-to-cover branches.
35
+ */
36
+ export function summarizeLastRun(run) {
37
+ if (!run)
38
+ return null;
39
+ return {
40
+ runId: run.id,
41
+ status: run.status,
42
+ trigger: run.trigger,
43
+ startedAt: run.startedAt,
44
+ ...(run.completedAt == null ? {} : { completedAt: run.completedAt }),
45
+ projectId: run.projectId,
46
+ conversationId: run.conversationId,
47
+ agentRunId: run.agentRunId,
48
+ ...(run.summary ? { summary: run.summary } : {}),
49
+ ...(run.error ? { error: run.error } : {}),
50
+ ...(run.errorCode ? { errorCode: run.errorCode } : {}),
51
+ };
52
+ }
53
+ function cloneContext(context) {
54
+ return {
55
+ ...(context.skillIds ? { skillIds: [...context.skillIds] } : {}),
56
+ ...(context.pluginIds ? { pluginIds: [...context.pluginIds] } : {}),
57
+ ...(context.mcpServerIds ? { mcpServerIds: [...context.mcpServerIds] } : {}),
58
+ ...(context.connectorIds ? { connectorIds: [...context.connectorIds] } : {}),
59
+ };
60
+ }
61
+ /** Defensive deep clone so a caller mutating a returned `Routine` (its `context` array fields, `schedule`, or `target`) cannot corrupt this store's internal state — the store's only copy is never handed out by reference. */
62
+ function cloneRoutine(routine) {
63
+ return {
64
+ ...routine,
65
+ context: cloneContext(routine.context),
66
+ schedule: { ...routine.schedule },
67
+ target: { ...routine.target },
68
+ };
69
+ }
70
+ /**
71
+ * Reference `RoutineStore` implementation: an in-process `Map` of routines plus a flat array of
72
+ * runs, no durable copy — matching `createInMemoryEventLog`'s own scope (a real persistent
73
+ * adapter is `@jini-ai/sqlite`'s job).
74
+ *
75
+ * @complexity `get`/`create`/`update`/`delete` are O(1). `list` is O(n log n) (stable id-sort for
76
+ * deterministic ordering). `listRuns`/`getLatestRun` are O(m log m) in the number of runs
77
+ * recorded for the routine (m), since each sorts by `startedAt` descending on every call rather
78
+ * than maintaining a secondary index — acceptable at this reference implementation's scale.
79
+ */
80
+ export function createInMemoryRoutineStore() {
81
+ const routines = new Map();
82
+ const runs = [];
83
+ function runsFor(routineId) {
84
+ return runs
85
+ .filter((run) => run.routineId === routineId)
86
+ .sort((a, b) => b.startedAt - a.startedAt);
87
+ }
88
+ async function withLastRun(routine) {
89
+ const latest = runsFor(routine.id)[0] ?? null;
90
+ return { ...cloneRoutine(routine), lastRun: summarizeLastRun(latest) };
91
+ }
92
+ return {
93
+ async list() {
94
+ const ids = Array.from(routines.keys()).sort();
95
+ return Promise.all(ids.map((id) => withLastRun(routines.get(id))));
96
+ },
97
+ async get(id) {
98
+ const routine = routines.get(id);
99
+ return routine ? withLastRun(routine) : null;
100
+ },
101
+ async create(input) {
102
+ const now = Date.now();
103
+ const routine = {
104
+ id: `routine-${randomUUID()}`,
105
+ name: input.name,
106
+ prompt: input.prompt,
107
+ schedule: input.schedule,
108
+ target: input.target,
109
+ skillId: input.skillId ?? null,
110
+ agentId: input.agentId ?? null,
111
+ context: input.context ?? {},
112
+ enabled: input.enabled ?? true,
113
+ nextRunAt: null,
114
+ lastRun: null,
115
+ createdAt: now,
116
+ updatedAt: now,
117
+ };
118
+ routines.set(routine.id, routine);
119
+ return withLastRun(routine);
120
+ },
121
+ async update(id, patch) {
122
+ const existing = routines.get(id);
123
+ if (!existing)
124
+ return null;
125
+ const updated = {
126
+ ...existing,
127
+ ...(patch.name !== undefined ? { name: patch.name } : {}),
128
+ ...(patch.prompt !== undefined ? { prompt: patch.prompt } : {}),
129
+ ...(patch.schedule !== undefined ? { schedule: patch.schedule } : {}),
130
+ ...(patch.target !== undefined ? { target: patch.target } : {}),
131
+ ...(patch.skillId !== undefined ? { skillId: patch.skillId } : {}),
132
+ ...(patch.agentId !== undefined ? { agentId: patch.agentId } : {}),
133
+ ...(patch.context !== undefined ? { context: patch.context } : {}),
134
+ ...(patch.enabled !== undefined ? { enabled: patch.enabled } : {}),
135
+ updatedAt: Date.now(),
136
+ };
137
+ routines.set(id, updated);
138
+ return withLastRun(updated);
139
+ },
140
+ async delete(id) {
141
+ return routines.delete(id);
142
+ },
143
+ async listRuns(routineId, limit) {
144
+ return runsFor(routineId).slice(0, limit);
145
+ },
146
+ async getLatestRun(routineId) {
147
+ return runsFor(routineId)[0] ?? null;
148
+ },
149
+ recordRun(run) {
150
+ if (runs.some((existing) => existing.id === run.id))
151
+ return false;
152
+ runs.push({ ...run });
153
+ return true;
154
+ },
155
+ patchRun(id, patch) {
156
+ const run = runs.find((candidate) => candidate.id === id);
157
+ if (run)
158
+ Object.assign(run, patch);
159
+ },
160
+ };
161
+ }
162
+ //# sourceMappingURL=routine-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routine-store.js","sourceRoot":"","sources":["../../src/routines/routine-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * DST-safe wall-clock schedule math + input validation for {@link ./scheduler.js}'s
3
+ * `RoutineService`. Ported verbatim (logic unchanged) from OD's `apps/daemon/src/routines.ts` —
4
+ * see that module's own header comment, quoted in the porting proposal
5
+ * (`ADS-memory/reports/proposals/PROP-http-route-packs-automation-routines-2026-07-21.md`), for
6
+ * why this is "real, non-trivial, DST-safe timezone math... genuinely hard to get right and not
7
+ * something to casually reinvent." Its only dependency is `Intl.DateTimeFormat`, which Node ships
8
+ * with full-icu by default — no OD coupling.
9
+ */
10
+ import type { RoutineProjectTarget, RoutineSchedule, Weekday } from './types.js';
11
+ /**
12
+ * Returns the wall-clock parts of `atUtc` rendered in `timezone`. Uses `Intl.DateTimeFormat`. If
13
+ * the timezone is invalid the formatter throws — callers catch upstream.
14
+ *
15
+ * Exported for the same reason as {@link tzWallToUtcCandidates}/{@link tzWallToUtcGapFallback}:
16
+ * its two defensive fallbacks (an Intl-reported hour of `"24"` at local midnight in some
17
+ * locale/zone combinations, and an unrecognized weekday abbreviation) are ICU-implementation
18
+ * edge cases no timezone on this test runner's ICU data naturally triggers — directly testable
19
+ * with a mocked `Intl.DateTimeFormat.prototype.formatToParts` instead of left unreachable.
20
+ */
21
+ export declare function partsInTimezone(timezone: string, atUtc: Date): {
22
+ year: number;
23
+ month: number;
24
+ day: number;
25
+ hour: number;
26
+ minute: number;
27
+ second: number;
28
+ weekday: Weekday;
29
+ };
30
+ /**
31
+ * Returns every UTC instant at which the wall clock in `timezone` reads the requested Y-M-D h:m,
32
+ * sorted ascending. Most days have exactly one match. On a fall-back transition day the requested
33
+ * time inside the repeated hour has two matches (one before the transition, one after); outside
34
+ * that hour it still has one. On a spring-forward gap the requested time inside the gap has zero
35
+ * matches — callers fall back to {@link tzWallToUtcGapFallback} to land on a post-gap instant the
36
+ * same day. Probes offsets at three reference points across the day so that both pre- and
37
+ * post-transition offsets are sampled regardless of which side of the transition `tentative`
38
+ * happens to land on. Returns `[]` if `timezone` is invalid.
39
+ *
40
+ * Exported (unlike this module's other low-level helpers) so its own `catch` branch is directly
41
+ * unit-testable: {@link nextWallTimeMatching}, this function's only real caller, already calls
42
+ * the unguarded `partsInTimezone` earlier in its own loop body, so an invalid timezone throws
43
+ * there first — this function's defensive catch is otherwise unreachable through that call path.
44
+ */
45
+ export declare function tzWallToUtcCandidates(timezone: string, year: number, month: number, day: number, hour: number, minute: number): Date[];
46
+ /**
47
+ * Spring-forward gap fallback: when the requested wall time doesn't exist in `timezone` on this
48
+ * day (clocks jumped over it), return the later of the two probe candidates. That instant has
49
+ * crossed the transition and renders as the first valid post-gap wall time, so a routine still
50
+ * fires today instead of firing an hour early before the gap. Returns `null` if `timezone` is
51
+ * invalid.
52
+ *
53
+ * Exported for the same reason as {@link tzWallToUtcCandidates} — its own `catch` branch is
54
+ * otherwise unreachable through {@link nextWallTimeMatching}'s call path.
55
+ */
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
+ /**
59
+ * Returns the next instant at which the wall-clock in `timezone` reads "HH:MM" on a day where
60
+ * `predicate(weekday)` holds. Walks forward at most 14 calendar days as a safety bound (covers
61
+ * any weekday-based pattern). Exported (unlike this module's other internal helpers) so its
62
+ * 14-day-exhaustion fallback is directly unit-testable: every `predicate` `nextRunAtForSchedule`
63
+ * actually passes (daily/weekdays/weekly) structurally guarantees a match within 7 days, making
64
+ * that branch otherwise unreachable through the public schedule-kind API alone.
65
+ */
66
+ export declare function nextWallTimeMatching(timezone: string, time: string, predicate: (weekday: Weekday) => boolean, now: Date): Date | null;
67
+ export declare function nextRunAtForSchedule(schedule: RoutineSchedule, now?: Date): Date | null;
68
+ export declare function isValidWallTime(time: string): boolean;
69
+ export declare function isValidTimezone(tz: string): boolean;
70
+ export declare function validateSchedule(schedule: RoutineSchedule): void;
71
+ export declare function validateTarget(target: RoutineProjectTarget): void;
72
+ //# sourceMappingURL=schedule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../src/routines/schedule.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAIjF;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,IAAI,GACV;IACD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB,CAoCA;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,IAAI,EAAE,CAoBR;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,IAAI,GAAG,IAAI,CAWb;AAwBD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAa,GAAG,IAAI,CAQtE;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,EACxC,GAAG,EAAE,IAAI,GACR,IAAI,GAAG,IAAI,CAuCb;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,OAAa,GAAG,IAAI,GAAG,IAAI,CAe7F;AAID,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMrD;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAQnD;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CA2BhE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAYjE"}