@the-open-engine/zeroshot 5.4.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 (295) hide show
  1. package/CHANGELOG.md +539 -0
  2. package/LICENSE +21 -0
  3. package/README.md +508 -0
  4. package/cli/commands/inspect-render.js +135 -0
  5. package/cli/commands/inspect.js +294 -0
  6. package/cli/commands/providers.js +149 -0
  7. package/cli/index.js +5431 -0
  8. package/cli/lib/first-run.js +211 -0
  9. package/cli/lib/update-checker.js +281 -0
  10. package/cli/message-formatter-utils.js +75 -0
  11. package/cli/message-formatters-normal.js +275 -0
  12. package/cli/message-formatters-watch.js +185 -0
  13. package/cluster-templates/base-templates/debug-workflow.json +422 -0
  14. package/cluster-templates/base-templates/full-workflow.json +727 -0
  15. package/cluster-templates/base-templates/heavy-validation.json +272 -0
  16. package/cluster-templates/base-templates/quick-validation.json +285 -0
  17. package/cluster-templates/base-templates/single-worker.json +71 -0
  18. package/cluster-templates/base-templates/worker-validator.json +230 -0
  19. package/cluster-templates/conductor-bootstrap.json +130 -0
  20. package/docker/zeroshot-cluster/Dockerfile +153 -0
  21. package/docker/zeroshot-cluster/pre-baked-deps.json +28 -0
  22. package/lib/agent-cli-provider/adapters/claude-parser.d.ts +3 -0
  23. package/lib/agent-cli-provider/adapters/claude-parser.d.ts.map +1 -0
  24. package/lib/agent-cli-provider/adapters/claude-parser.js +122 -0
  25. package/lib/agent-cli-provider/adapters/claude-parser.js.map +1 -0
  26. package/lib/agent-cli-provider/adapters/claude-recovery.d.ts +15 -0
  27. package/lib/agent-cli-provider/adapters/claude-recovery.d.ts.map +1 -0
  28. package/lib/agent-cli-provider/adapters/claude-recovery.js +165 -0
  29. package/lib/agent-cli-provider/adapters/claude-recovery.js.map +1 -0
  30. package/lib/agent-cli-provider/adapters/claude.d.ts +3 -0
  31. package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -0
  32. package/lib/agent-cli-provider/adapters/claude.js +181 -0
  33. package/lib/agent-cli-provider/adapters/claude.js.map +1 -0
  34. package/lib/agent-cli-provider/adapters/codex-parser.d.ts +3 -0
  35. package/lib/agent-cli-provider/adapters/codex-parser.d.ts.map +1 -0
  36. package/lib/agent-cli-provider/adapters/codex-parser.js +192 -0
  37. package/lib/agent-cli-provider/adapters/codex-parser.js.map +1 -0
  38. package/lib/agent-cli-provider/adapters/codex.d.ts +3 -0
  39. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -0
  40. package/lib/agent-cli-provider/adapters/codex.js +151 -0
  41. package/lib/agent-cli-provider/adapters/codex.js.map +1 -0
  42. package/lib/agent-cli-provider/adapters/common.d.ts +28 -0
  43. package/lib/agent-cli-provider/adapters/common.d.ts.map +1 -0
  44. package/lib/agent-cli-provider/adapters/common.js +71 -0
  45. package/lib/agent-cli-provider/adapters/common.js.map +1 -0
  46. package/lib/agent-cli-provider/adapters/gemini.d.ts +3 -0
  47. package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -0
  48. package/lib/agent-cli-provider/adapters/gemini.js +176 -0
  49. package/lib/agent-cli-provider/adapters/gemini.js.map +1 -0
  50. package/lib/agent-cli-provider/adapters/index.d.ts +9 -0
  51. package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -0
  52. package/lib/agent-cli-provider/adapters/index.js +95 -0
  53. package/lib/agent-cli-provider/adapters/index.js.map +1 -0
  54. package/lib/agent-cli-provider/adapters/opencode.d.ts +3 -0
  55. package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -0
  56. package/lib/agent-cli-provider/adapters/opencode.js +228 -0
  57. package/lib/agent-cli-provider/adapters/opencode.js.map +1 -0
  58. package/lib/agent-cli-provider/contract-actions.d.ts +5 -0
  59. package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -0
  60. package/lib/agent-cli-provider/contract-actions.js +140 -0
  61. package/lib/agent-cli-provider/contract-actions.js.map +1 -0
  62. package/lib/agent-cli-provider/contract-env.d.ts +11 -0
  63. package/lib/agent-cli-provider/contract-env.d.ts.map +1 -0
  64. package/lib/agent-cli-provider/contract-env.js +113 -0
  65. package/lib/agent-cli-provider/contract-env.js.map +1 -0
  66. package/lib/agent-cli-provider/contract-envelope.d.ts +49 -0
  67. package/lib/agent-cli-provider/contract-envelope.d.ts.map +1 -0
  68. package/lib/agent-cli-provider/contract-envelope.js +52 -0
  69. package/lib/agent-cli-provider/contract-envelope.js.map +1 -0
  70. package/lib/agent-cli-provider/contract-errors.d.ts +22 -0
  71. package/lib/agent-cli-provider/contract-errors.d.ts.map +1 -0
  72. package/lib/agent-cli-provider/contract-errors.js +71 -0
  73. package/lib/agent-cli-provider/contract-errors.js.map +1 -0
  74. package/lib/agent-cli-provider/contract-fallback.d.ts +7 -0
  75. package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -0
  76. package/lib/agent-cli-provider/contract-fallback.js +102 -0
  77. package/lib/agent-cli-provider/contract-fallback.js.map +1 -0
  78. package/lib/agent-cli-provider/contract-invoke.d.ts +5 -0
  79. package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -0
  80. package/lib/agent-cli-provider/contract-invoke.js +89 -0
  81. package/lib/agent-cli-provider/contract-invoke.js.map +1 -0
  82. package/lib/agent-cli-provider/contract-options.d.ts +3 -0
  83. package/lib/agent-cli-provider/contract-options.d.ts.map +1 -0
  84. package/lib/agent-cli-provider/contract-options.js +140 -0
  85. package/lib/agent-cli-provider/contract-options.js.map +1 -0
  86. package/lib/agent-cli-provider/contract-parse.d.ts +15 -0
  87. package/lib/agent-cli-provider/contract-parse.d.ts.map +1 -0
  88. package/lib/agent-cli-provider/contract-parse.js +81 -0
  89. package/lib/agent-cli-provider/contract-parse.js.map +1 -0
  90. package/lib/agent-cli-provider/contract-support.d.ts +18 -0
  91. package/lib/agent-cli-provider/contract-support.d.ts.map +1 -0
  92. package/lib/agent-cli-provider/contract-support.js +154 -0
  93. package/lib/agent-cli-provider/contract-support.js.map +1 -0
  94. package/lib/agent-cli-provider/contract.d.ts +13 -0
  95. package/lib/agent-cli-provider/contract.d.ts.map +1 -0
  96. package/lib/agent-cli-provider/contract.js +30 -0
  97. package/lib/agent-cli-provider/contract.js.map +1 -0
  98. package/lib/agent-cli-provider/env-safety.d.ts +11 -0
  99. package/lib/agent-cli-provider/env-safety.d.ts.map +1 -0
  100. package/lib/agent-cli-provider/env-safety.js +83 -0
  101. package/lib/agent-cli-provider/env-safety.js.map +1 -0
  102. package/lib/agent-cli-provider/errors.d.ts +5 -0
  103. package/lib/agent-cli-provider/errors.d.ts.map +1 -0
  104. package/lib/agent-cli-provider/errors.js +115 -0
  105. package/lib/agent-cli-provider/errors.js.map +1 -0
  106. package/lib/agent-cli-provider/executable.d.ts +3 -0
  107. package/lib/agent-cli-provider/executable.d.ts.map +1 -0
  108. package/lib/agent-cli-provider/executable.js +24 -0
  109. package/lib/agent-cli-provider/executable.js.map +1 -0
  110. package/lib/agent-cli-provider/index.d.ts +8 -0
  111. package/lib/agent-cli-provider/index.d.ts.map +1 -0
  112. package/lib/agent-cli-provider/index.js +31 -0
  113. package/lib/agent-cli-provider/index.js.map +1 -0
  114. package/lib/agent-cli-provider/invoke-evidence.d.ts +4 -0
  115. package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -0
  116. package/lib/agent-cli-provider/invoke-evidence.js +17 -0
  117. package/lib/agent-cli-provider/invoke-evidence.js.map +1 -0
  118. package/lib/agent-cli-provider/json.d.ts +16 -0
  119. package/lib/agent-cli-provider/json.d.ts.map +1 -0
  120. package/lib/agent-cli-provider/json.js +110 -0
  121. package/lib/agent-cli-provider/json.js.map +1 -0
  122. package/lib/agent-cli-provider/log-prefix.d.ts +2 -0
  123. package/lib/agent-cli-provider/log-prefix.d.ts.map +1 -0
  124. package/lib/agent-cli-provider/log-prefix.js +22 -0
  125. package/lib/agent-cli-provider/log-prefix.js.map +1 -0
  126. package/lib/agent-cli-provider/process-runner.d.ts +17 -0
  127. package/lib/agent-cli-provider/process-runner.d.ts.map +1 -0
  128. package/lib/agent-cli-provider/process-runner.js +89 -0
  129. package/lib/agent-cli-provider/process-runner.js.map +1 -0
  130. package/lib/agent-cli-provider/redaction.d.ts +9 -0
  131. package/lib/agent-cli-provider/redaction.d.ts.map +1 -0
  132. package/lib/agent-cli-provider/redaction.js +227 -0
  133. package/lib/agent-cli-provider/redaction.js.map +1 -0
  134. package/lib/agent-cli-provider/schema.d.ts +5 -0
  135. package/lib/agent-cli-provider/schema.d.ts.map +1 -0
  136. package/lib/agent-cli-provider/schema.js +136 -0
  137. package/lib/agent-cli-provider/schema.js.map +1 -0
  138. package/lib/agent-cli-provider/single-agent-runtime.d.ts +15 -0
  139. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -0
  140. package/lib/agent-cli-provider/single-agent-runtime.js +228 -0
  141. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -0
  142. package/lib/agent-cli-provider/types.d.ts +194 -0
  143. package/lib/agent-cli-provider/types.d.ts.map +1 -0
  144. package/lib/agent-cli-provider/types.js +12 -0
  145. package/lib/agent-cli-provider/types.js.map +1 -0
  146. package/lib/completion.js +174 -0
  147. package/lib/detached-startup.js +220 -0
  148. package/lib/docker-config.js +220 -0
  149. package/lib/git-remote-utils.js +165 -0
  150. package/lib/id-detector.js +55 -0
  151. package/lib/provider-defaults.js +62 -0
  152. package/lib/provider-detection.js +59 -0
  153. package/lib/provider-names.js +57 -0
  154. package/lib/repo-settings.js +69 -0
  155. package/lib/settings/claude-auth.js +78 -0
  156. package/lib/settings.js +542 -0
  157. package/lib/start-cluster.js +321 -0
  158. package/lib/stream-json-parser.js +67 -0
  159. package/package.json +162 -0
  160. package/scripts/fix-node-pty-permissions.js +75 -0
  161. package/scripts/record-demo.sh +279 -0
  162. package/scripts/setup-merge-queue.sh +170 -0
  163. package/scripts/test-install.sh +40 -0
  164. package/scripts/validate-templates.js +107 -0
  165. package/src/agent/agent-config.js +266 -0
  166. package/src/agent/agent-context-builder.js +189 -0
  167. package/src/agent/agent-context-sections.js +338 -0
  168. package/src/agent/agent-context-sources.js +147 -0
  169. package/src/agent/agent-hook-executor.js +721 -0
  170. package/src/agent/agent-input-injector.js +141 -0
  171. package/src/agent/agent-lifecycle.js +982 -0
  172. package/src/agent/agent-quality-gate-schema.js +93 -0
  173. package/src/agent/agent-quality-gates-context.js +51 -0
  174. package/src/agent/agent-stuck-detector.js +256 -0
  175. package/src/agent/agent-task-executor.js +2028 -0
  176. package/src/agent/agent-trigger-evaluator.js +67 -0
  177. package/src/agent/context-metrics.js +160 -0
  178. package/src/agent/context-pack-builder.js +367 -0
  179. package/src/agent/context-replay-policy.js +51 -0
  180. package/src/agent/guidance-queue.js +77 -0
  181. package/src/agent/output-extraction.js +367 -0
  182. package/src/agent/output-reformatter.js +175 -0
  183. package/src/agent/pr-verification.js +653 -0
  184. package/src/agent/rate-limit-backoff.js +82 -0
  185. package/src/agent/schema-utils.js +146 -0
  186. package/src/agent/validation-platform.js +35 -0
  187. package/src/agent-cli-provider/adapters/claude-parser.ts +133 -0
  188. package/src/agent-cli-provider/adapters/claude-recovery.ts +203 -0
  189. package/src/agent-cli-provider/adapters/claude.ts +247 -0
  190. package/src/agent-cli-provider/adapters/codex-parser.ts +211 -0
  191. package/src/agent-cli-provider/adapters/codex.ts +217 -0
  192. package/src/agent-cli-provider/adapters/common.ts +124 -0
  193. package/src/agent-cli-provider/adapters/gemini.ts +243 -0
  194. package/src/agent-cli-provider/adapters/index.ts +126 -0
  195. package/src/agent-cli-provider/adapters/opencode.ts +286 -0
  196. package/src/agent-cli-provider/contract-actions.ts +150 -0
  197. package/src/agent-cli-provider/contract-env.ts +111 -0
  198. package/src/agent-cli-provider/contract-envelope.ts +110 -0
  199. package/src/agent-cli-provider/contract-errors.ts +66 -0
  200. package/src/agent-cli-provider/contract-fallback.ts +121 -0
  201. package/src/agent-cli-provider/contract-invoke.ts +104 -0
  202. package/src/agent-cli-provider/contract-options.ts +173 -0
  203. package/src/agent-cli-provider/contract-parse.ts +94 -0
  204. package/src/agent-cli-provider/contract-support.ts +167 -0
  205. package/src/agent-cli-provider/contract.ts +56 -0
  206. package/src/agent-cli-provider/env-safety.ts +82 -0
  207. package/src/agent-cli-provider/errors.ts +122 -0
  208. package/src/agent-cli-provider/executable.ts +24 -0
  209. package/src/agent-cli-provider/index.ts +83 -0
  210. package/src/agent-cli-provider/invoke-evidence.ts +18 -0
  211. package/src/agent-cli-provider/json.ts +114 -0
  212. package/src/agent-cli-provider/log-prefix.ts +20 -0
  213. package/src/agent-cli-provider/process-runner.ts +145 -0
  214. package/src/agent-cli-provider/redaction.ts +282 -0
  215. package/src/agent-cli-provider/schema.ts +115 -0
  216. package/src/agent-cli-provider/single-agent-runtime.ts +311 -0
  217. package/src/agent-cli-provider/types.ts +237 -0
  218. package/src/agent-wrapper.js +615 -0
  219. package/src/agents/git-pusher-template.js +705 -0
  220. package/src/attach/attach-client.js +438 -0
  221. package/src/attach/attach-server.js +543 -0
  222. package/src/attach/index.js +37 -0
  223. package/src/attach/protocol.js +220 -0
  224. package/src/attach/ring-buffer.js +121 -0
  225. package/src/attach/send-input.js +88 -0
  226. package/src/attach/socket-discovery.js +267 -0
  227. package/src/claude-task-runner.js +661 -0
  228. package/src/config-router.js +89 -0
  229. package/src/config-validator.js +2202 -0
  230. package/src/copy-worker.js +43 -0
  231. package/src/guidance-topics.js +10 -0
  232. package/src/input-helpers.js +65 -0
  233. package/src/isolation-manager.js +1734 -0
  234. package/src/issue-providers/README.md +305 -0
  235. package/src/issue-providers/azure-devops-provider.js +307 -0
  236. package/src/issue-providers/base-provider.js +232 -0
  237. package/src/issue-providers/github-provider.js +210 -0
  238. package/src/issue-providers/gitlab-provider.js +262 -0
  239. package/src/issue-providers/index.js +196 -0
  240. package/src/issue-providers/jira-provider.js +260 -0
  241. package/src/ledger.js +692 -0
  242. package/src/lib/gc.js +232 -0
  243. package/src/lib/safe-exec.js +88 -0
  244. package/src/logic-engine.js +201 -0
  245. package/src/message-buffer.js +81 -0
  246. package/src/message-bus-bridge.js +144 -0
  247. package/src/message-bus.js +256 -0
  248. package/src/name-generator.js +232 -0
  249. package/src/orchestrator.js +3924 -0
  250. package/src/preflight.js +712 -0
  251. package/src/process-metrics.js +608 -0
  252. package/src/providers/anthropic/index.js +3 -0
  253. package/src/providers/base-provider.js +355 -0
  254. package/src/providers/capabilities.js +60 -0
  255. package/src/providers/google/index.js +3 -0
  256. package/src/providers/index.js +293 -0
  257. package/src/providers/openai/index.js +3 -0
  258. package/src/providers/opencode/index.js +3 -0
  259. package/src/quality-gates.js +143 -0
  260. package/src/schemas/sub-cluster.js +208 -0
  261. package/src/state-snapshot.js +398 -0
  262. package/src/state-snapshotter.js +142 -0
  263. package/src/status-footer.js +1026 -0
  264. package/src/sub-cluster-wrapper.js +693 -0
  265. package/src/task-runner.js +30 -0
  266. package/src/template-resolver.js +425 -0
  267. package/src/template-validation/index.js +338 -0
  268. package/src/template-validation/simulate-consensus-gates.js +324 -0
  269. package/src/template-validation/simulate-random-topology.js +541 -0
  270. package/src/template-validation/simulate-two-stage-validation.js +270 -0
  271. package/src/worktree-claude-config.js +135 -0
  272. package/src/worktree-tooling-env.js +150 -0
  273. package/task-lib/attachable-watcher.js +381 -0
  274. package/task-lib/commands/clean.js +50 -0
  275. package/task-lib/commands/episodes.js +105 -0
  276. package/task-lib/commands/get-log-path.js +23 -0
  277. package/task-lib/commands/kill.js +32 -0
  278. package/task-lib/commands/list.js +105 -0
  279. package/task-lib/commands/logs.js +439 -0
  280. package/task-lib/commands/resume.js +42 -0
  281. package/task-lib/commands/run.js +57 -0
  282. package/task-lib/commands/schedule.js +105 -0
  283. package/task-lib/commands/scheduler-cmd.js +96 -0
  284. package/task-lib/commands/schedules.js +148 -0
  285. package/task-lib/commands/status.js +44 -0
  286. package/task-lib/commands/unschedule.js +16 -0
  287. package/task-lib/completion.js +9 -0
  288. package/task-lib/config.js +11 -0
  289. package/task-lib/name-generator.js +230 -0
  290. package/task-lib/package.json +3 -0
  291. package/task-lib/provider-helper-runtime.js +29 -0
  292. package/task-lib/runner.js +190 -0
  293. package/task-lib/scheduler.js +252 -0
  294. package/task-lib/store.js +529 -0
  295. package/task-lib/watcher.js +305 -0
@@ -0,0 +1,529 @@
1
+ /**
2
+ * Task Store - SQLite-backed storage for tasks and schedules
3
+ *
4
+ * Uses WAL mode for concurrent access - no file locks needed.
5
+ * Multiple processes can read/write simultaneously without contention.
6
+ */
7
+
8
+ import { existsSync, mkdirSync } from 'fs';
9
+ import { join } from 'path';
10
+ import Database from 'better-sqlite3';
11
+ import { TASKS_DIR, LOGS_DIR } from './config.js';
12
+ import { generateName } from './name-generator.js';
13
+
14
+ const DB_FILE = join(TASKS_DIR, 'store.db');
15
+
16
+ /** @type {Database.Database | null} */
17
+ let db = null;
18
+
19
+ /**
20
+ * Get or create the database connection
21
+ * @returns {Database.Database}
22
+ */
23
+ function getDb() {
24
+ if (db) return db;
25
+
26
+ ensureDirs();
27
+
28
+ db = new Database(DB_FILE, { timeout: 5000 });
29
+
30
+ // WAL mode for concurrent access - this is the key fix
31
+ db.pragma('journal_mode = WAL');
32
+ db.pragma('synchronous = NORMAL');
33
+
34
+ // Create tables
35
+ db.exec(`
36
+ CREATE TABLE IF NOT EXISTS tasks (
37
+ id TEXT PRIMARY KEY,
38
+ prompt TEXT,
39
+ full_prompt TEXT,
40
+ cwd TEXT,
41
+ status TEXT NOT NULL DEFAULT 'pending',
42
+ pid INTEGER,
43
+ session_id TEXT,
44
+ log_file TEXT,
45
+ created_at TEXT NOT NULL,
46
+ updated_at TEXT NOT NULL,
47
+ exit_code INTEGER,
48
+ error TEXT,
49
+ provider TEXT,
50
+ model TEXT,
51
+ schedule_id TEXT,
52
+ socket_path TEXT,
53
+ attachable INTEGER DEFAULT 0
54
+ );
55
+
56
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
57
+ CREATE INDEX IF NOT EXISTS idx_tasks_created_at ON tasks(created_at);
58
+
59
+ CREATE TABLE IF NOT EXISTS schedules (
60
+ id TEXT PRIMARY KEY,
61
+ cron TEXT NOT NULL,
62
+ prompt TEXT NOT NULL,
63
+ cwd TEXT,
64
+ model TEXT,
65
+ model_level TEXT,
66
+ reasoning_effort TEXT,
67
+ provider TEXT,
68
+ enabled INTEGER DEFAULT 1,
69
+ last_run TEXT,
70
+ next_run TEXT,
71
+ created_at TEXT NOT NULL,
72
+ updated_at TEXT NOT NULL
73
+ );
74
+
75
+ CREATE INDEX IF NOT EXISTS idx_schedules_enabled ON schedules(enabled);
76
+ `);
77
+
78
+ return db;
79
+ }
80
+
81
+ export function ensureDirs() {
82
+ if (!existsSync(TASKS_DIR)) mkdirSync(TASKS_DIR, { recursive: true });
83
+ if (!existsSync(LOGS_DIR)) mkdirSync(LOGS_DIR, { recursive: true });
84
+ }
85
+
86
+ // ============================================================================
87
+ // Tasks
88
+ // ============================================================================
89
+
90
+ /**
91
+ * Convert DB row to task object (camelCase)
92
+ */
93
+ function rowToTask(row) {
94
+ if (!row) return null;
95
+ return {
96
+ id: row.id,
97
+ prompt: row.prompt,
98
+ fullPrompt: row.full_prompt,
99
+ cwd: row.cwd,
100
+ status: row.status,
101
+ pid: row.pid,
102
+ sessionId: row.session_id,
103
+ logFile: row.log_file,
104
+ createdAt: row.created_at,
105
+ updatedAt: row.updated_at,
106
+ exitCode: row.exit_code,
107
+ error: row.error,
108
+ provider: row.provider,
109
+ model: row.model,
110
+ scheduleId: row.schedule_id,
111
+ socketPath: row.socket_path,
112
+ attachable: Boolean(row.attachable),
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Load all tasks as object keyed by id
118
+ * @returns {Object.<string, Object>}
119
+ */
120
+ export function loadTasks() {
121
+ const rows = getDb().prepare('SELECT * FROM tasks ORDER BY created_at DESC').all();
122
+ const tasks = {};
123
+ for (const row of rows) {
124
+ const task = rowToTask(row);
125
+ tasks[task.id] = task;
126
+ }
127
+ return tasks;
128
+ }
129
+
130
+ /**
131
+ * Save all tasks (replaces entire store - for migration compatibility)
132
+ * @param {Object.<string, Object>} tasks
133
+ */
134
+ export function saveTasks(tasks) {
135
+ const database = getDb();
136
+ const insert = database.prepare(`
137
+ INSERT OR REPLACE INTO tasks (
138
+ id, prompt, full_prompt, cwd, status, pid, session_id, log_file,
139
+ created_at, updated_at, exit_code, error, provider, model,
140
+ schedule_id, socket_path, attachable
141
+ ) VALUES (
142
+ @id, @prompt, @fullPrompt, @cwd, @status, @pid, @sessionId, @logFile,
143
+ @createdAt, @updatedAt, @exitCode, @error, @provider, @model,
144
+ @scheduleId, @socketPath, @attachable
145
+ )
146
+ `);
147
+
148
+ const insertMany = database.transaction((tasksObj) => {
149
+ // Clear existing
150
+ database.prepare('DELETE FROM tasks').run();
151
+ // Insert all
152
+ for (const task of Object.values(tasksObj)) {
153
+ insert.run({
154
+ id: task.id,
155
+ prompt: task.prompt || null,
156
+ fullPrompt: task.fullPrompt || null,
157
+ cwd: task.cwd || null,
158
+ status: task.status || 'pending',
159
+ pid: task.pid || null,
160
+ sessionId: task.sessionId || null,
161
+ logFile: task.logFile || null,
162
+ createdAt: task.createdAt || new Date().toISOString(),
163
+ updatedAt: task.updatedAt || new Date().toISOString(),
164
+ exitCode: task.exitCode ?? null,
165
+ error: task.error || null,
166
+ provider: task.provider || null,
167
+ model: task.model || null,
168
+ scheduleId: task.scheduleId || null,
169
+ socketPath: task.socketPath || null,
170
+ attachable: task.attachable ? 1 : 0,
171
+ });
172
+ }
173
+ });
174
+
175
+ insertMany(tasks);
176
+ }
177
+
178
+ /**
179
+ * For API compatibility - just runs the modifier synchronously
180
+ * SQLite WAL handles concurrency, no lock needed
181
+ * @param {Function} modifier
182
+ * @returns {any}
183
+ */
184
+ export function withTasksLock(modifier) {
185
+ const tasks = loadTasks();
186
+ const result = modifier(tasks);
187
+ saveTasks(tasks);
188
+ return result;
189
+ }
190
+
191
+ /**
192
+ * Get a single task by id
193
+ * @param {string} id
194
+ * @returns {Object|null}
195
+ */
196
+ export function getTask(id) {
197
+ const row = getDb().prepare('SELECT * FROM tasks WHERE id = ?').get(id);
198
+ return rowToTask(row);
199
+ }
200
+
201
+ /**
202
+ * Update a task
203
+ * @param {string} id
204
+ * @param {Object} updates
205
+ * @returns {Object|null}
206
+ */
207
+ export function updateTask(id, updates) {
208
+ const existing = getTask(id);
209
+ if (!existing) return null;
210
+
211
+ const updated = {
212
+ ...existing,
213
+ ...updates,
214
+ updatedAt: new Date().toISOString(),
215
+ };
216
+
217
+ getDb()
218
+ .prepare(
219
+ `
220
+ UPDATE tasks SET
221
+ prompt = @prompt,
222
+ full_prompt = @fullPrompt,
223
+ cwd = @cwd,
224
+ status = @status,
225
+ pid = @pid,
226
+ session_id = @sessionId,
227
+ log_file = @logFile,
228
+ updated_at = @updatedAt,
229
+ exit_code = @exitCode,
230
+ error = @error,
231
+ provider = @provider,
232
+ model = @model,
233
+ schedule_id = @scheduleId,
234
+ socket_path = @socketPath,
235
+ attachable = @attachable
236
+ WHERE id = @id
237
+ `
238
+ )
239
+ .run({
240
+ id: updated.id,
241
+ prompt: updated.prompt || null,
242
+ fullPrompt: updated.fullPrompt || null,
243
+ cwd: updated.cwd || null,
244
+ status: updated.status || 'pending',
245
+ pid: updated.pid || null,
246
+ sessionId: updated.sessionId || null,
247
+ logFile: updated.logFile || null,
248
+ updatedAt: updated.updatedAt,
249
+ exitCode: updated.exitCode ?? null,
250
+ error: updated.error || null,
251
+ provider: updated.provider || null,
252
+ model: updated.model || null,
253
+ scheduleId: updated.scheduleId || null,
254
+ socketPath: updated.socketPath || null,
255
+ attachable: updated.attachable ? 1 : 0,
256
+ });
257
+
258
+ return updated;
259
+ }
260
+
261
+ /**
262
+ * Add a new task
263
+ * @param {Object} task
264
+ * @returns {Object}
265
+ */
266
+ export function addTask(task) {
267
+ const now = new Date().toISOString();
268
+ const fullTask = {
269
+ ...task,
270
+ createdAt: task.createdAt || now,
271
+ updatedAt: task.updatedAt || now,
272
+ };
273
+
274
+ getDb()
275
+ .prepare(
276
+ `
277
+ INSERT INTO tasks (
278
+ id, prompt, full_prompt, cwd, status, pid, session_id, log_file,
279
+ created_at, updated_at, exit_code, error, provider, model,
280
+ schedule_id, socket_path, attachable
281
+ ) VALUES (
282
+ @id, @prompt, @fullPrompt, @cwd, @status, @pid, @sessionId, @logFile,
283
+ @createdAt, @updatedAt, @exitCode, @error, @provider, @model,
284
+ @scheduleId, @socketPath, @attachable
285
+ )
286
+ `
287
+ )
288
+ .run({
289
+ id: fullTask.id,
290
+ prompt: fullTask.prompt || null,
291
+ fullPrompt: fullTask.fullPrompt || null,
292
+ cwd: fullTask.cwd || null,
293
+ status: fullTask.status || 'pending',
294
+ pid: fullTask.pid || null,
295
+ sessionId: fullTask.sessionId || null,
296
+ logFile: fullTask.logFile || null,
297
+ createdAt: fullTask.createdAt,
298
+ updatedAt: fullTask.updatedAt,
299
+ exitCode: fullTask.exitCode ?? null,
300
+ error: fullTask.error || null,
301
+ provider: fullTask.provider || null,
302
+ model: fullTask.model || null,
303
+ scheduleId: fullTask.scheduleId || null,
304
+ socketPath: fullTask.socketPath || null,
305
+ attachable: fullTask.attachable ? 1 : 0,
306
+ });
307
+
308
+ return fullTask;
309
+ }
310
+
311
+ /**
312
+ * Remove a task
313
+ * @param {string} id
314
+ */
315
+ export function removeTask(id) {
316
+ getDb().prepare('DELETE FROM tasks WHERE id = ?').run(id);
317
+ }
318
+
319
+ export function generateId() {
320
+ return generateName('task');
321
+ }
322
+
323
+ export function generateScheduleId() {
324
+ return generateName('sched');
325
+ }
326
+
327
+ // ============================================================================
328
+ // Schedules
329
+ // ============================================================================
330
+
331
+ /**
332
+ * Convert DB row to schedule object (camelCase)
333
+ */
334
+ function rowToSchedule(row) {
335
+ if (!row) return null;
336
+ return {
337
+ id: row.id,
338
+ cron: row.cron,
339
+ prompt: row.prompt,
340
+ cwd: row.cwd,
341
+ model: row.model,
342
+ modelLevel: row.model_level,
343
+ reasoningEffort: row.reasoning_effort,
344
+ provider: row.provider,
345
+ enabled: Boolean(row.enabled),
346
+ lastRun: row.last_run,
347
+ nextRun: row.next_run,
348
+ createdAt: row.created_at,
349
+ updatedAt: row.updated_at,
350
+ };
351
+ }
352
+
353
+ /**
354
+ * Load all schedules as object keyed by id
355
+ * @returns {Object.<string, Object>}
356
+ */
357
+ export function loadSchedules() {
358
+ const rows = getDb().prepare('SELECT * FROM schedules ORDER BY created_at DESC').all();
359
+ const schedules = {};
360
+ for (const row of rows) {
361
+ const schedule = rowToSchedule(row);
362
+ schedules[schedule.id] = schedule;
363
+ }
364
+ return schedules;
365
+ }
366
+
367
+ /**
368
+ * Save all schedules (replaces entire store)
369
+ * @param {Object.<string, Object>} schedules
370
+ */
371
+ export function saveSchedules(schedules) {
372
+ const database = getDb();
373
+ const insert = database.prepare(`
374
+ INSERT OR REPLACE INTO schedules (
375
+ id, cron, prompt, cwd, model, model_level, reasoning_effort,
376
+ provider, enabled, last_run, next_run, created_at, updated_at
377
+ ) VALUES (
378
+ @id, @cron, @prompt, @cwd, @model, @modelLevel, @reasoningEffort,
379
+ @provider, @enabled, @lastRun, @nextRun, @createdAt, @updatedAt
380
+ )
381
+ `);
382
+
383
+ const insertMany = database.transaction((schedulesObj) => {
384
+ database.prepare('DELETE FROM schedules').run();
385
+ for (const schedule of Object.values(schedulesObj)) {
386
+ insert.run({
387
+ id: schedule.id,
388
+ cron: schedule.cron,
389
+ prompt: schedule.prompt,
390
+ cwd: schedule.cwd || null,
391
+ model: schedule.model || null,
392
+ modelLevel: schedule.modelLevel || null,
393
+ reasoningEffort: schedule.reasoningEffort || null,
394
+ provider: schedule.provider || null,
395
+ enabled: schedule.enabled ? 1 : 0,
396
+ lastRun: schedule.lastRun || null,
397
+ nextRun: schedule.nextRun || null,
398
+ createdAt: schedule.createdAt || new Date().toISOString(),
399
+ updatedAt: schedule.updatedAt || new Date().toISOString(),
400
+ });
401
+ }
402
+ });
403
+
404
+ insertMany(schedules);
405
+ }
406
+
407
+ /**
408
+ * Get a single schedule by id
409
+ * @param {string} id
410
+ * @returns {Object|null}
411
+ */
412
+ export function getSchedule(id) {
413
+ const row = getDb().prepare('SELECT * FROM schedules WHERE id = ?').get(id);
414
+ return rowToSchedule(row);
415
+ }
416
+
417
+ /**
418
+ * Add a new schedule
419
+ * @param {Object} schedule
420
+ * @returns {Object}
421
+ */
422
+ export function addSchedule(schedule) {
423
+ const now = new Date().toISOString();
424
+ const fullSchedule = {
425
+ ...schedule,
426
+ createdAt: schedule.createdAt || now,
427
+ updatedAt: schedule.updatedAt || now,
428
+ };
429
+
430
+ getDb()
431
+ .prepare(
432
+ `
433
+ INSERT INTO schedules (
434
+ id, cron, prompt, cwd, model, model_level, reasoning_effort,
435
+ provider, enabled, last_run, next_run, created_at, updated_at
436
+ ) VALUES (
437
+ @id, @cron, @prompt, @cwd, @model, @modelLevel, @reasoningEffort,
438
+ @provider, @enabled, @lastRun, @nextRun, @createdAt, @updatedAt
439
+ )
440
+ `
441
+ )
442
+ .run({
443
+ id: fullSchedule.id,
444
+ cron: fullSchedule.cron,
445
+ prompt: fullSchedule.prompt,
446
+ cwd: fullSchedule.cwd || null,
447
+ model: fullSchedule.model || null,
448
+ modelLevel: fullSchedule.modelLevel || null,
449
+ reasoningEffort: fullSchedule.reasoningEffort || null,
450
+ provider: fullSchedule.provider || null,
451
+ enabled: fullSchedule.enabled !== false ? 1 : 0,
452
+ lastRun: fullSchedule.lastRun || null,
453
+ nextRun: fullSchedule.nextRun || null,
454
+ createdAt: fullSchedule.createdAt,
455
+ updatedAt: fullSchedule.updatedAt,
456
+ });
457
+
458
+ return fullSchedule;
459
+ }
460
+
461
+ /**
462
+ * Update a schedule
463
+ * @param {string} id
464
+ * @param {Object} updates
465
+ * @returns {Object|null}
466
+ */
467
+ export function updateSchedule(id, updates) {
468
+ const existing = getSchedule(id);
469
+ if (!existing) return null;
470
+
471
+ const updated = {
472
+ ...existing,
473
+ ...updates,
474
+ updatedAt: new Date().toISOString(),
475
+ };
476
+
477
+ getDb()
478
+ .prepare(
479
+ `
480
+ UPDATE schedules SET
481
+ cron = @cron,
482
+ prompt = @prompt,
483
+ cwd = @cwd,
484
+ model = @model,
485
+ model_level = @modelLevel,
486
+ reasoning_effort = @reasoningEffort,
487
+ provider = @provider,
488
+ enabled = @enabled,
489
+ last_run = @lastRun,
490
+ next_run = @nextRun,
491
+ updated_at = @updatedAt
492
+ WHERE id = @id
493
+ `
494
+ )
495
+ .run({
496
+ id: updated.id,
497
+ cron: updated.cron,
498
+ prompt: updated.prompt,
499
+ cwd: updated.cwd || null,
500
+ model: updated.model || null,
501
+ modelLevel: updated.modelLevel || null,
502
+ reasoningEffort: updated.reasoningEffort || null,
503
+ provider: updated.provider || null,
504
+ enabled: updated.enabled ? 1 : 0,
505
+ lastRun: updated.lastRun || null,
506
+ nextRun: updated.nextRun || null,
507
+ updatedAt: updated.updatedAt,
508
+ });
509
+
510
+ return updated;
511
+ }
512
+
513
+ /**
514
+ * Remove a schedule
515
+ * @param {string} id
516
+ */
517
+ export function removeSchedule(id) {
518
+ getDb().prepare('DELETE FROM schedules WHERE id = ?').run(id);
519
+ }
520
+
521
+ /**
522
+ * Close the database connection (for cleanup)
523
+ */
524
+ export function closeDb() {
525
+ if (db) {
526
+ db.close();
527
+ db = null;
528
+ }
529
+ }