steroids-cli 0.9.10 → 0.9.12

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 (55) hide show
  1. package/dist/commands/runners-parallel.d.ts.map +1 -1
  2. package/dist/commands/runners-parallel.js +16 -4
  3. package/dist/commands/runners-parallel.js.map +1 -1
  4. package/dist/parallel/merge-commit-checks.d.ts +6 -0
  5. package/dist/parallel/merge-commit-checks.d.ts.map +1 -0
  6. package/dist/parallel/merge-commit-checks.js +28 -0
  7. package/dist/parallel/merge-commit-checks.js.map +1 -0
  8. package/dist/parallel/merge-process.d.ts +21 -0
  9. package/dist/parallel/merge-process.d.ts.map +1 -0
  10. package/dist/parallel/merge-process.js +104 -0
  11. package/dist/parallel/merge-process.js.map +1 -0
  12. package/dist/parallel/merge-sealing.d.ts +11 -0
  13. package/dist/parallel/merge-sealing.d.ts.map +1 -0
  14. package/dist/parallel/merge-sealing.js +64 -0
  15. package/dist/parallel/merge-sealing.js.map +1 -0
  16. package/dist/parallel/merge-validation.d.ts +6 -0
  17. package/dist/parallel/merge-validation.d.ts.map +1 -0
  18. package/dist/parallel/merge-validation.js +57 -0
  19. package/dist/parallel/merge-validation.js.map +1 -0
  20. package/dist/parallel/merge-workspace.d.ts +8 -0
  21. package/dist/parallel/merge-workspace.d.ts.map +1 -0
  22. package/dist/parallel/merge-workspace.js +37 -0
  23. package/dist/parallel/merge-workspace.js.map +1 -0
  24. package/dist/parallel/merge.d.ts +10 -5
  25. package/dist/parallel/merge.d.ts.map +1 -1
  26. package/dist/parallel/merge.js +22 -246
  27. package/dist/parallel/merge.js.map +1 -1
  28. package/dist/runners/daemon.d.ts.map +1 -1
  29. package/dist/runners/daemon.js +16 -0
  30. package/dist/runners/daemon.js.map +1 -1
  31. package/dist/runners/wakeup-checks.d.ts +14 -0
  32. package/dist/runners/wakeup-checks.d.ts.map +1 -0
  33. package/dist/runners/wakeup-checks.js +85 -0
  34. package/dist/runners/wakeup-checks.js.map +1 -0
  35. package/dist/runners/wakeup-reconcile.d.ts +10 -0
  36. package/dist/runners/wakeup-reconcile.d.ts.map +1 -0
  37. package/dist/runners/wakeup-reconcile.js +84 -0
  38. package/dist/runners/wakeup-reconcile.js.map +1 -0
  39. package/dist/runners/wakeup-runner.d.ts +18 -0
  40. package/dist/runners/wakeup-runner.d.ts.map +1 -0
  41. package/dist/runners/wakeup-runner.js +47 -0
  42. package/dist/runners/wakeup-runner.js.map +1 -0
  43. package/dist/runners/wakeup-sanitise.d.ts +24 -0
  44. package/dist/runners/wakeup-sanitise.d.ts.map +1 -0
  45. package/dist/runners/wakeup-sanitise.js +247 -0
  46. package/dist/runners/wakeup-sanitise.js.map +1 -0
  47. package/dist/runners/wakeup-timing.d.ts +12 -0
  48. package/dist/runners/wakeup-timing.d.ts.map +1 -0
  49. package/dist/runners/wakeup-timing.js +37 -0
  50. package/dist/runners/wakeup-timing.js.map +1 -0
  51. package/dist/runners/wakeup.d.ts +7 -8
  52. package/dist/runners/wakeup.d.ts.map +1 -1
  53. package/dist/runners/wakeup.js +26 -433
  54. package/dist/runners/wakeup.js.map +1 -1
  55. package/package.json +1 -1
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ /**
3
+ * Parallel session reconciliation for wakeup
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.releaseExpiredWorkstreamLeases = releaseExpiredWorkstreamLeases;
7
+ exports.reconcileParallelSessionRecovery = reconcileParallelSessionRecovery;
8
+ function releaseExpiredWorkstreamLeases(db) {
9
+ const result = db.prepare(`UPDATE workstreams
10
+ SET runner_id = NULL,
11
+ lease_expires_at = NULL
12
+ WHERE status = 'running'
13
+ AND lease_expires_at IS NOT NULL
14
+ AND lease_expires_at <= datetime('now')`).run();
15
+ return result.changes;
16
+ }
17
+ function reconcileParallelSessionRecovery(db, projectPath) {
18
+ const sessions = db
19
+ .prepare(`SELECT id
20
+ FROM parallel_sessions
21
+ WHERE project_path = ?
22
+ AND status NOT IN ('completed', 'failed', 'aborted', 'blocked_validation', 'blocked_recovery')`)
23
+ .all(projectPath);
24
+ let scheduledRetries = 0;
25
+ let blockedWorkstreams = 0;
26
+ for (const session of sessions) {
27
+ let blockedInSession = 0;
28
+ const candidates = db
29
+ .prepare(`SELECT id, recovery_attempts
30
+ FROM workstreams
31
+ WHERE session_id = ?
32
+ AND status = 'running'
33
+ AND (lease_expires_at IS NULL OR lease_expires_at <= datetime('now'))
34
+ AND (next_retry_at IS NULL OR next_retry_at <= datetime('now'))`)
35
+ .all(session.id);
36
+ for (const candidate of candidates) {
37
+ const nextAttempts = (candidate.recovery_attempts ?? 0) + 1;
38
+ if (nextAttempts >= 5) {
39
+ db.prepare(`UPDATE workstreams
40
+ SET status = 'failed',
41
+ recovery_attempts = ?,
42
+ next_retry_at = NULL,
43
+ last_reconcile_action = 'blocked_recovery',
44
+ last_reconciled_at = datetime('now')
45
+ WHERE id = ?`).run(nextAttempts, candidate.id);
46
+ blockedWorkstreams += 1;
47
+ blockedInSession += 1;
48
+ continue;
49
+ }
50
+ const backoffMinutes = Math.min(2 ** nextAttempts, 30);
51
+ db.prepare(`UPDATE workstreams
52
+ SET recovery_attempts = ?,
53
+ next_retry_at = datetime('now', ?),
54
+ last_reconcile_action = 'retry_scheduled',
55
+ last_reconciled_at = datetime('now')
56
+ WHERE id = ?`).run(nextAttempts, `+${backoffMinutes} minutes`, candidate.id);
57
+ scheduledRetries += 1;
58
+ }
59
+ if (blockedInSession > 0) {
60
+ db.prepare(`UPDATE parallel_sessions
61
+ SET status = 'blocked_recovery',
62
+ completed_at = NULL
63
+ WHERE id = ?`).run(session.id);
64
+ continue;
65
+ }
66
+ // If all workstreams are in a terminal state but the session is still marked
67
+ // running (e.g. because autoMergeOnCompletion crashed before it could call
68
+ // updateParallelSessionStatus), close it out now so wakeup doesn't block.
69
+ const nonTerminal = db.prepare(`SELECT COUNT(*) as count FROM workstreams
70
+ WHERE session_id = ? AND status NOT IN ('completed', 'failed', 'aborted')`).get(session.id);
71
+ if (nonTerminal.count === 0) {
72
+ const total = db.prepare(`SELECT COUNT(*) as count FROM workstreams WHERE session_id = ?`).get(session.id);
73
+ if (total.count > 0) {
74
+ // All workstreams finished — mark session completed so wakeup can move on.
75
+ db.prepare(`UPDATE parallel_sessions
76
+ SET status = 'completed',
77
+ completed_at = COALESCE(completed_at, datetime('now'))
78
+ WHERE id = ?`).run(session.id);
79
+ }
80
+ }
81
+ }
82
+ return { scheduledRetries, blockedWorkstreams };
83
+ }
84
+ //# sourceMappingURL=wakeup-reconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakeup-reconcile.js","sourceRoot":"","sources":["../../src/runners/wakeup-reconcile.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAIH,wEAaC;AAED,4EA+FC;AA9GD,SAAgB,8BAA8B,CAC5C,EAA+C;IAE/C,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CACvB;;;;;+CAK2C,CAC5C,CAAC,GAAG,EAAE,CAAC;IAER,OAAO,MAAM,CAAC,OAAO,CAAC;AACxB,CAAC;AAED,SAAgB,gCAAgC,CAC9C,EAA+C,EAC/C,WAAmB;IAEnB,MAAM,QAAQ,GAAG,EAAE;SAChB,OAAO,CACN;;;wGAGkG,CACnG;SACA,GAAG,CAAC,WAAW,CAA0B,CAAC;IAE7C,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,IAAI,kBAAkB,GAAG,CAAC,CAAC;IAE3B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,gBAAgB,GAAG,CAAC,CAAC;QACzB,MAAM,UAAU,GAAG,EAAE;aAClB,OAAO,CACN;;;;;2EAKmE,CACpE;aACA,GAAG,CAAC,OAAO,CAAC,EAAE,CAAqD,CAAC;QAEvE,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,YAAY,GAAG,CAAC,SAAS,CAAC,iBAAiB,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YAE5D,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;gBACtB,EAAE,CAAC,OAAO,CACR;;;;;;wBAMc,CACf,CAAC,GAAG,CAAC,YAAY,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;gBAClC,kBAAkB,IAAI,CAAC,CAAC;gBACxB,gBAAgB,IAAI,CAAC,CAAC;gBACtB,SAAS;YACX,CAAC;YAED,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,YAAY,EAAE,EAAE,CAAC,CAAC;YACvD,EAAE,CAAC,OAAO,CACR;;;;;sBAKc,CACf,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,cAAc,UAAU,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;YAChE,gBAAgB,IAAI,CAAC,CAAC;QACxB,CAAC;QAED,IAAI,gBAAgB,GAAG,CAAC,EAAE,CAAC;YACzB,EAAE,CAAC,OAAO,CACR;;;sBAGc,CACf,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAClB,SAAS;QACX,CAAC;QAED,6EAA6E;QAC7E,2EAA2E;QAC3E,0EAA0E;QAC1E,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,CAC5B;iFAC2E,CAC5E,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAsB,CAAC;QAEvC,IAAI,WAAW,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,KAAK,GAAG,EAAE,CAAC,OAAO,CACtB,gEAAgE,CACjE,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAsB,CAAC;YAEvC,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;gBACpB,2EAA2E;gBAC3E,EAAE,CAAC,OAAO,CACR;;;wBAGc,CACf,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,CAAC;AAClD,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Runner management for wakeup
3
+ */
4
+ /**
5
+ * Kill a process by PID
6
+ */
7
+ export declare function killProcess(pid: number): boolean;
8
+ /**
9
+ * Start a new runner daemon
10
+ * Uses 'steroids runners start --detach' so the runner registers in the global DB
11
+ * and updates heartbeat, allowing hasActiveRunnerForProject() to detect it.
12
+ * When runners.parallel.enabled is true, launches a parallel session instead.
13
+ */
14
+ export declare function startRunner(projectPath: string): {
15
+ pid: number;
16
+ parallel?: boolean;
17
+ } | null;
18
+ //# sourceMappingURL=wakeup-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakeup-runner.d.ts","sourceRoot":"","sources":["../../src/runners/wakeup-runner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOhD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAoB3F"}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ /**
3
+ * Runner management for wakeup
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.killProcess = killProcess;
7
+ exports.startRunner = startRunner;
8
+ const node_child_process_1 = require("node:child_process");
9
+ const loader_js_1 = require("../config/loader.js");
10
+ /**
11
+ * Kill a process by PID
12
+ */
13
+ function killProcess(pid) {
14
+ try {
15
+ process.kill(pid, 'SIGTERM');
16
+ return true;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ /**
23
+ * Start a new runner daemon
24
+ * Uses 'steroids runners start --detach' so the runner registers in the global DB
25
+ * and updates heartbeat, allowing hasActiveRunnerForProject() to detect it.
26
+ * When runners.parallel.enabled is true, launches a parallel session instead.
27
+ */
28
+ function startRunner(projectPath) {
29
+ try {
30
+ const config = (0, loader_js_1.loadConfig)(projectPath);
31
+ const parallelEnabled = config.runners?.parallel?.enabled === true;
32
+ const args = parallelEnabled
33
+ ? ['runners', 'start', '--parallel', '--project', projectPath]
34
+ : ['runners', 'start', '--detach', '--project', projectPath];
35
+ const child = (0, node_child_process_1.spawn)('steroids', args, {
36
+ cwd: projectPath,
37
+ detached: true,
38
+ stdio: 'ignore',
39
+ });
40
+ child.unref();
41
+ return { pid: child.pid ?? 0, parallel: parallelEnabled };
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ //# sourceMappingURL=wakeup-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakeup-runner.js","sourceRoot":"","sources":["../../src/runners/wakeup-runner.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAQH,kCAOC;AAQD,kCAoBC;AAzCD,2DAA2C;AAC3C,mDAAiD;AAEjD;;GAEG;AACH,SAAgB,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAgB,WAAW,CAAC,WAAmB;IAC7C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAA,sBAAU,EAAC,WAAW,CAAC,CAAC;QACvC,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;QAEnE,MAAM,IAAI,GAAG,eAAe;YAC1B,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,CAAC;YAC9D,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;QAE/D,MAAM,KAAK,GAAG,IAAA,0BAAK,EAAC,UAAU,EAAE,IAAI,EAAE;YACpC,GAAG,EAAE,WAAW;YAChB,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,QAAQ;SAChB,CAAC,CAAC;QAEH,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Periodic state sanitisation for projects in wakeup
3
+ */
4
+ import { openDatabase } from '../database/connection.js';
5
+ import { openGlobalDatabase } from './global-db.js';
6
+ export interface SanitiseSettings {
7
+ enabled: boolean;
8
+ intervalMinutes: number;
9
+ staleInvocationTimeoutSec: number;
10
+ }
11
+ export interface SanitiseSummary {
12
+ ran: boolean;
13
+ reason: string;
14
+ recoveredApprovals: number;
15
+ recoveredRejects: number;
16
+ closedStaleInvocations: number;
17
+ releasedTaskLocks: number;
18
+ releasedSectionLocks: number;
19
+ }
20
+ export declare function getSanitiseSettings(projectPath: string): SanitiseSettings;
21
+ export declare function shouldRunPeriodicSanitise(db: ReturnType<typeof openGlobalDatabase>['db'], projectPath: string, intervalMinutes: number): boolean;
22
+ export declare function runPeriodicSanitiseForProject(globalDb: ReturnType<typeof openGlobalDatabase>['db'], projectDb: ReturnType<typeof openDatabase>['db'], projectPath: string, dryRun: boolean): SanitiseSummary;
23
+ export declare function sanitisedActionCount(summary: SanitiseSummary): number;
24
+ //# sourceMappingURL=wakeup-sanitise.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakeup-sanitise.d.ts","sourceRoot":"","sources":["../../src/runners/wakeup-sanitise.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGpD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,yBAAyB,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAKD,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAezE;AAMD,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,IAAI,CAAC,EAC/C,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,GACtB,OAAO,CAkBT;AAmOD,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,IAAI,CAAC,EACrD,SAAS,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,IAAI,CAAC,EAChD,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,OAAO,GACd,eAAe,CAuCjB;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAQrE"}
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ /**
3
+ * Periodic state sanitisation for projects in wakeup
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getSanitiseSettings = getSanitiseSettings;
7
+ exports.shouldRunPeriodicSanitise = shouldRunPeriodicSanitise;
8
+ exports.runPeriodicSanitiseForProject = runPeriodicSanitiseForProject;
9
+ exports.sanitisedActionCount = sanitisedActionCount;
10
+ const node_fs_1 = require("node:fs");
11
+ const node_path_1 = require("node:path");
12
+ const node_fs_2 = require("node:fs");
13
+ const loader_js_1 = require("../config/loader.js");
14
+ const DEFAULT_SANITISE_INTERVAL_MINUTES = 5;
15
+ const DEFAULT_SANITISE_INVOCATION_TIMEOUT_SEC = 1800;
16
+ function getSanitiseSettings(projectPath) {
17
+ const config = (0, loader_js_1.loadConfig)(projectPath);
18
+ const health = config.health ?? {};
19
+ const enabled = health.sanitiseEnabled ?? true;
20
+ const intervalMinutes = Math.max(1, Number(health.sanitiseIntervalMinutes ?? DEFAULT_SANITISE_INTERVAL_MINUTES));
21
+ const staleInvocationTimeoutSec = Math.max(60, Number(health.sanitiseInvocationTimeoutSec ?? DEFAULT_SANITISE_INVOCATION_TIMEOUT_SEC));
22
+ return { enabled, intervalMinutes, staleInvocationTimeoutSec };
23
+ }
24
+ function getSanitiseSchemaKey(projectPath) {
25
+ return `wakeup_sanitise_last_run::${projectPath}`;
26
+ }
27
+ function shouldRunPeriodicSanitise(db, projectPath, intervalMinutes) {
28
+ const key = getSanitiseSchemaKey(projectPath);
29
+ const row = db
30
+ .prepare('SELECT value FROM _global_schema WHERE key = ?')
31
+ .get(key);
32
+ if (!row?.value) {
33
+ return true;
34
+ }
35
+ const due = db.prepare(`SELECT CASE
36
+ WHEN datetime(?) <= datetime('now', ?)
37
+ THEN 1 ELSE 0
38
+ END AS due`).get(row.value, `-${intervalMinutes} minutes`);
39
+ return (due?.due ?? 0) === 1;
40
+ }
41
+ function markPeriodicSanitiseRun(db, projectPath) {
42
+ db.prepare(`INSERT INTO _global_schema (key, value) VALUES (?, datetime('now'))
43
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(getSanitiseSchemaKey(projectPath));
44
+ }
45
+ function parseReviewerDecisionFromLog(projectPath, invocationId) {
46
+ const logPath = (0, node_path_1.join)(projectPath, '.steroids', 'invocations', `${invocationId}.log`);
47
+ if (!(0, node_fs_1.existsSync)(logPath)) {
48
+ return null;
49
+ }
50
+ try {
51
+ const raw = (0, node_fs_2.readFileSync)(logPath, 'utf-8');
52
+ if (raw.includes('DECISION: APPROVE')) {
53
+ return 'approve';
54
+ }
55
+ if (raw.includes('DECISION: REJECT')) {
56
+ return 'reject';
57
+ }
58
+ }
59
+ catch {
60
+ return null;
61
+ }
62
+ return null;
63
+ }
64
+ function sanitiseProjectState(globalDb, projectDb, projectPath, dryRun, staleInvocationTimeoutSec) {
65
+ const summary = {
66
+ ran: true,
67
+ reason: 'ok',
68
+ recoveredApprovals: 0,
69
+ recoveredRejects: 0,
70
+ closedStaleInvocations: 0,
71
+ releasedTaskLocks: 0,
72
+ releasedSectionLocks: 0,
73
+ };
74
+ const staleCutoffMs = Date.now() - staleInvocationTimeoutSec * 1000;
75
+ const activeRunnerTaskRows = globalDb
76
+ .prepare(`SELECT current_task_id, parallel_session_id
77
+ FROM runners
78
+ WHERE project_path = ?
79
+ AND status = 'running'
80
+ AND heartbeat_at > datetime('now', '-5 minutes')
81
+ AND (current_task_id IS NOT NULL OR parallel_session_id IS NOT NULL)`)
82
+ .all(projectPath);
83
+ const activeTaskIds = new Set(activeRunnerTaskRows
84
+ .map((row) => row.current_task_id)
85
+ .filter((taskId) => typeof taskId === 'string' && taskId.length > 0));
86
+ const hasActiveParallelRunner = activeRunnerTaskRows.some((row) => typeof row.parallel_session_id === 'string' && row.parallel_session_id.length > 0);
87
+ let hasActiveMergeLock = false;
88
+ try {
89
+ const mergeLockRows = projectDb
90
+ .prepare('SELECT expires_at FROM merge_locks')
91
+ .all();
92
+ const nowMs = Date.now();
93
+ hasActiveMergeLock = mergeLockRows.some((row) => {
94
+ const expiresAtMs = Date.parse(row.expires_at);
95
+ return Number.isFinite(expiresAtMs) && expiresAtMs > nowMs;
96
+ });
97
+ }
98
+ catch {
99
+ hasActiveMergeLock = false;
100
+ }
101
+ const hasActiveParallelContext = hasActiveParallelRunner || hasActiveMergeLock;
102
+ const staleInvocations = projectDb
103
+ .prepare(`SELECT i.id, i.task_id, i.role, i.started_at_ms, t.status AS task_status
104
+ FROM task_invocations i
105
+ LEFT JOIN tasks t ON t.id = i.task_id
106
+ WHERE i.status = 'running'
107
+ AND i.started_at_ms IS NOT NULL
108
+ AND i.started_at_ms <= ?
109
+ ORDER BY i.started_at_ms ASC`)
110
+ .all(staleCutoffMs);
111
+ for (const row of staleInvocations) {
112
+ if (activeTaskIds.has(row.task_id) || hasActiveParallelContext) {
113
+ continue;
114
+ }
115
+ const completedAtMs = Date.now();
116
+ const durationMs = Math.max(0, completedAtMs - row.started_at_ms);
117
+ const reviewerDecision = row.role === 'reviewer'
118
+ ? parseReviewerDecisionFromLog(projectPath, row.id)
119
+ : null;
120
+ if (!dryRun) {
121
+ if (reviewerDecision === 'approve' && row.task_status === 'review') {
122
+ projectDb
123
+ .prepare(`UPDATE task_invocations
124
+ SET status = 'completed',
125
+ success = 1,
126
+ timed_out = 0,
127
+ exit_code = 0,
128
+ completed_at_ms = ?,
129
+ duration_ms = ?,
130
+ error = COALESCE(error, 'Recovered by periodic sanitise (review approve token found).')
131
+ WHERE id = ? AND status = 'running'`)
132
+ .run(completedAtMs, durationMs, row.id);
133
+ projectDb
134
+ .prepare(`UPDATE tasks
135
+ SET status = 'completed',
136
+ updated_at = datetime('now')
137
+ WHERE id = ? AND status = 'review'`)
138
+ .run(row.task_id);
139
+ projectDb
140
+ .prepare(`INSERT INTO audit (task_id, from_status, to_status, actor, actor_type, notes, created_at)
141
+ SELECT ?, 'review', 'completed', 'orchestrator', 'orchestrator',
142
+ 'Recovered by periodic sanitise from reviewer decision log (DECISION: APPROVE).',
143
+ datetime('now')
144
+ WHERE EXISTS (SELECT 1 FROM tasks WHERE id = ? AND status = 'completed')`)
145
+ .run(row.task_id, row.task_id);
146
+ }
147
+ else if (reviewerDecision === 'reject' && row.task_status === 'review') {
148
+ projectDb
149
+ .prepare(`UPDATE task_invocations
150
+ SET status = 'completed',
151
+ success = 1,
152
+ timed_out = 0,
153
+ exit_code = 0,
154
+ completed_at_ms = ?,
155
+ duration_ms = ?,
156
+ error = COALESCE(error, 'Recovered by periodic sanitise (review reject token found).')
157
+ WHERE id = ? AND status = 'running'`)
158
+ .run(completedAtMs, durationMs, row.id);
159
+ projectDb
160
+ .prepare(`UPDATE tasks
161
+ SET status = 'in_progress',
162
+ rejection_count = COALESCE(rejection_count, 0) + 1,
163
+ updated_at = datetime('now')
164
+ WHERE id = ? AND status = 'review'`)
165
+ .run(row.task_id);
166
+ projectDb
167
+ .prepare(`INSERT INTO audit (task_id, from_status, to_status, actor, actor_type, notes, created_at)
168
+ SELECT ?, 'review', 'in_progress', 'orchestrator', 'orchestrator',
169
+ 'Recovered by periodic sanitise from reviewer decision log (DECISION: REJECT).',
170
+ datetime('now')
171
+ WHERE EXISTS (SELECT 1 FROM tasks WHERE id = ? AND status = 'in_progress')`)
172
+ .run(row.task_id, row.task_id);
173
+ }
174
+ else {
175
+ projectDb
176
+ .prepare(`UPDATE task_invocations
177
+ SET status = 'failed',
178
+ success = 0,
179
+ timed_out = 1,
180
+ exit_code = 1,
181
+ completed_at_ms = ?,
182
+ duration_ms = ?,
183
+ error = COALESCE(error, 'Periodic sanitise closed stale running invocation.')
184
+ WHERE id = ? AND status = 'running'`)
185
+ .run(completedAtMs, durationMs, row.id);
186
+ }
187
+ }
188
+ if (reviewerDecision === 'approve' && row.task_status === 'review') {
189
+ summary.recoveredApprovals += 1;
190
+ }
191
+ else if (reviewerDecision === 'reject' && row.task_status === 'review') {
192
+ summary.recoveredRejects += 1;
193
+ }
194
+ else {
195
+ summary.closedStaleInvocations += 1;
196
+ }
197
+ }
198
+ if (!dryRun) {
199
+ const releasedTaskLocks = projectDb
200
+ .prepare(`DELETE FROM task_locks WHERE expires_at <= datetime('now')`)
201
+ .run();
202
+ summary.releasedTaskLocks = releasedTaskLocks.changes;
203
+ const releasedSectionLocks = projectDb
204
+ .prepare(`DELETE FROM section_locks WHERE expires_at <= datetime('now')`)
205
+ .run();
206
+ summary.releasedSectionLocks = releasedSectionLocks.changes;
207
+ }
208
+ return summary;
209
+ }
210
+ function runPeriodicSanitiseForProject(globalDb, projectDb, projectPath, dryRun) {
211
+ const settings = getSanitiseSettings(projectPath);
212
+ if (!settings.enabled) {
213
+ return {
214
+ ran: false,
215
+ reason: 'disabled',
216
+ recoveredApprovals: 0,
217
+ recoveredRejects: 0,
218
+ closedStaleInvocations: 0,
219
+ releasedTaskLocks: 0,
220
+ releasedSectionLocks: 0,
221
+ };
222
+ }
223
+ if (!shouldRunPeriodicSanitise(globalDb, projectPath, settings.intervalMinutes)) {
224
+ return {
225
+ ran: false,
226
+ reason: 'interval_not_due',
227
+ recoveredApprovals: 0,
228
+ recoveredRejects: 0,
229
+ closedStaleInvocations: 0,
230
+ releasedTaskLocks: 0,
231
+ releasedSectionLocks: 0,
232
+ };
233
+ }
234
+ const summary = sanitiseProjectState(globalDb, projectDb, projectPath, dryRun, settings.staleInvocationTimeoutSec);
235
+ if (!dryRun) {
236
+ markPeriodicSanitiseRun(globalDb, projectPath);
237
+ }
238
+ return summary;
239
+ }
240
+ function sanitisedActionCount(summary) {
241
+ return (summary.recoveredApprovals +
242
+ summary.recoveredRejects +
243
+ summary.closedStaleInvocations +
244
+ summary.releasedTaskLocks +
245
+ summary.releasedSectionLocks);
246
+ }
247
+ //# sourceMappingURL=wakeup-sanitise.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakeup-sanitise.js","sourceRoot":"","sources":["../../src/runners/wakeup-sanitise.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Wakeup timing and state tracking
3
+ */
4
+ /**
5
+ * Record the last wakeup invocation time
6
+ */
7
+ export declare function recordWakeupTime(): void;
8
+ /**
9
+ * Get the last wakeup invocation time
10
+ */
11
+ export declare function getLastWakeupTime(): string | null;
12
+ //# sourceMappingURL=wakeup-timing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakeup-timing.d.ts","sourceRoot":"","sources":["../../src/runners/wakeup-timing.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAUvC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAUjD"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ /**
3
+ * Wakeup timing and state tracking
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.recordWakeupTime = recordWakeupTime;
7
+ exports.getLastWakeupTime = getLastWakeupTime;
8
+ const global_db_js_1 = require("./global-db.js");
9
+ /**
10
+ * Record the last wakeup invocation time
11
+ */
12
+ function recordWakeupTime() {
13
+ const { db, close } = (0, global_db_js_1.openGlobalDatabase)();
14
+ try {
15
+ db.prepare(`INSERT INTO _global_schema (key, value) VALUES ('last_wakeup_at', datetime('now'))
16
+ ON CONFLICT(key) DO UPDATE SET value = datetime('now')`).run();
17
+ }
18
+ finally {
19
+ close();
20
+ }
21
+ }
22
+ /**
23
+ * Get the last wakeup invocation time
24
+ */
25
+ function getLastWakeupTime() {
26
+ const { db, close } = (0, global_db_js_1.openGlobalDatabase)();
27
+ try {
28
+ const row = db
29
+ .prepare("SELECT value FROM _global_schema WHERE key = 'last_wakeup_at'")
30
+ .get();
31
+ return row?.value ?? null;
32
+ }
33
+ finally {
34
+ close();
35
+ }
36
+ }
37
+ //# sourceMappingURL=wakeup-timing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakeup-timing.js","sourceRoot":"","sources":["../../src/runners/wakeup-timing.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAOH,4CAUC;AAKD,8CAUC;AA9BD,iDAAoD;AAEpD;;GAEG;AACH,SAAgB,gBAAgB;IAC9B,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,IAAA,iCAAkB,GAAE,CAAC;IAC3C,IAAI,CAAC;QACH,EAAE,CAAC,OAAO,CACR;8DACwD,CACzD,CAAC,GAAG,EAAE,CAAC;IACV,CAAC;YAAS,CAAC;QACT,KAAK,EAAE,CAAC;IACV,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB;IAC/B,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,IAAA,iCAAkB,GAAE,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE;aACX,OAAO,CAAC,+DAA+D,CAAC;aACxE,GAAG,EAAmC,CAAC;QAC1C,OAAO,GAAG,EAAE,KAAK,IAAI,IAAI,CAAC;IAC5B,CAAC;YAAS,CAAC;QACT,KAAK,EAAE,CAAC;IACV,CAAC;AACH,CAAC"}
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Cron wake-up command for restarting stale/dead runners
3
3
  */
4
+ import { hasActiveRunnerForProject, hasActiveParallelSessionForProject } from './wakeup-checks.js';
5
+ import { getLastWakeupTime } from './wakeup-timing.js';
4
6
  export interface WakeupOptions {
5
7
  quiet?: boolean;
6
8
  dryRun?: boolean;
@@ -18,16 +20,13 @@ export interface WakeupResult {
18
20
  deletedInvocationLogs?: number;
19
21
  sanitisedActions?: number;
20
22
  }
23
+ export { getLastWakeupTime, hasActiveRunnerForProject, hasActiveParallelSessionForProject };
21
24
  /**
22
- * Check if there's an active runner for a specific project
23
- * Exported for use in daemon startup checks
25
+ * Main wake-up function
26
+ * Called by cron every minute to ensure runners are healthy
27
+ * Iterates over ALL registered projects and starts runners as needed
28
+ * Returns per-project results
24
29
  */
25
- export declare function hasActiveRunnerForProject(projectPath: string): boolean;
26
- export declare function hasActiveParallelSessionForProject(projectPath: string): boolean;
27
- /**
28
- * Get the last wakeup invocation time
29
- */
30
- export declare function getLastWakeupTime(): string | null;
31
30
  export declare function wakeup(options?: WakeupOptions): Promise<WakeupResult[]>;
32
31
  /**
33
32
  * Check if wake-up is needed without taking action
@@ -1 +1 @@
1
- {"version":3,"file":"wakeup.d.ts","sourceRoot":"","sources":["../../src/runners/wakeup.ts"],"names":[],"mappings":"AAAA;;GAEG;AAeH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,aAAa,CAAC;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+BAA+B,CAAC,EAAE,OAAO,CAAC;IAC1C,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAiDD;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAiBtE;AAED,wBAAgB,kCAAkC,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAgB/E;AA0dD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAUjD;AAED,wBAAsB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAyQjF;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IACjD,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAyCD"}
1
+ {"version":3,"file":"wakeup.d.ts","sourceRoot":"","sources":["../../src/runners/wakeup.ts"],"names":[],"mappings":"AAAA;;GAEG;AAmBH,OAAO,EAEL,yBAAyB,EACzB,kCAAkC,EACnC,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAoB,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEzE,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,aAAa,CAAC;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+BAA+B,CAAC,EAAE,OAAO,CAAC;IAC1C,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,kCAAkC,EAAE,CAAC;AAE5F;;;;;GAKG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAgRjF;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IACjD,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAyCD"}