issue-flow 0.18.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -100
- package/dist/agent-EWKA7357.js +32 -0
- package/dist/{analyze-Q4ONIWRZ.js → analyze-Z7ODC27F.js} +42 -41
- package/dist/analyze-Z7ODC27F.js.map +1 -0
- package/dist/{apply-EEFPY2W4.js → apply-VNSWQS6O.js} +22 -24
- package/dist/apply-VNSWQS6O.js.map +1 -0
- package/dist/artifacts-AMJQJFFV.js +82 -0
- package/dist/artifacts-AMJQJFFV.js.map +1 -0
- package/dist/{bench-WWWXLTFH.js → bench-ZJEIACUK.js} +42 -42
- package/dist/{chunk-RYOUZ6EK.js → chunk-23VHVVKW.js} +3 -3
- package/dist/chunk-2EYLOS5I.js +99 -0
- package/dist/chunk-2EYLOS5I.js.map +1 -0
- package/dist/{chunk-XUEMATVY.js → chunk-2EZW4X57.js} +28 -25
- package/dist/chunk-2EZW4X57.js.map +1 -0
- package/dist/{chunk-LGHND47K.js → chunk-2Q6PG3MT.js} +15 -15
- package/dist/chunk-2Q6PG3MT.js.map +1 -0
- package/dist/{chunk-C5TL7T4V.js → chunk-2YM2VH6R.js} +2 -2
- package/dist/{chunk-OODXFCQV.js → chunk-2YYP7AE6.js} +14 -16
- package/dist/{chunk-OODXFCQV.js.map → chunk-2YYP7AE6.js.map} +1 -1
- package/dist/chunk-3JD25HHJ.js +278 -0
- package/dist/chunk-3JD25HHJ.js.map +1 -0
- package/dist/{chunk-PMLE7KAQ.js → chunk-46S7APLQ.js} +30 -14
- package/dist/chunk-46S7APLQ.js.map +1 -0
- package/dist/chunk-6AOZCWN2.js +21 -0
- package/dist/chunk-6AOZCWN2.js.map +1 -0
- package/dist/{chunk-QPGEFLMY.js → chunk-7CMVL4X6.js} +7 -7
- package/dist/chunk-7MJGYPHF.js +1571 -0
- package/dist/chunk-7MJGYPHF.js.map +1 -0
- package/dist/chunk-7ZZUSMBJ.js +35 -0
- package/dist/chunk-7ZZUSMBJ.js.map +1 -0
- package/dist/{chunk-DUS5ZNKL.js → chunk-DIMDAYZS.js} +7 -6
- package/dist/{chunk-DGRTJD5R.js → chunk-DXH4TQRB.js} +7 -7
- package/dist/{chunk-CRGIQFUQ.js → chunk-E2H3JV2E.js} +3 -1
- package/dist/chunk-E2H3JV2E.js.map +1 -0
- package/dist/{chunk-YUNBOUQH.js → chunk-ERAG5IQB.js} +8 -8
- package/dist/{chunk-ZVZMWNCG.js → chunk-F2NFJ27Z.js} +2 -2
- package/dist/{chunk-SOM4QA6C.js → chunk-GD7JZMSJ.js} +93 -18
- package/dist/chunk-GD7JZMSJ.js.map +1 -0
- package/dist/{chunk-6K2LCFG6.js → chunk-HEHHQU4G.js} +70 -81
- package/dist/chunk-HEHHQU4G.js.map +1 -0
- package/dist/{chunk-34OCSKOB.js → chunk-HHXWAWU6.js} +159 -114
- package/dist/chunk-HHXWAWU6.js.map +1 -0
- package/dist/{chunk-E4I6MMHD.js → chunk-HWWB22HC.js} +152 -100
- package/dist/chunk-HWWB22HC.js.map +1 -0
- package/dist/chunk-IMB6FA2F.js +673 -0
- package/dist/chunk-IMB6FA2F.js.map +1 -0
- package/dist/{chunk-ECU7YO7S.js → chunk-IQYXXFRC.js} +22 -6
- package/dist/chunk-IQYXXFRC.js.map +1 -0
- package/dist/chunk-J6VJ5M2U.js +1201 -0
- package/dist/chunk-J6VJ5M2U.js.map +1 -0
- package/dist/{chunk-QJ44VG5G.js → chunk-KDQRNVBZ.js} +3 -3
- package/dist/{chunk-7UMMFOL2.js → chunk-LRWIP7QS.js} +39 -24
- package/dist/chunk-LRWIP7QS.js.map +1 -0
- package/dist/{chunk-LI35PIIS.js → chunk-LYC57OXF.js} +20 -117
- package/dist/chunk-LYC57OXF.js.map +1 -0
- package/dist/{chunk-HAKDLR6N.js → chunk-MAJ6QDRL.js} +65 -78
- package/dist/chunk-MAJ6QDRL.js.map +1 -0
- package/dist/{chunk-CWSQVHVV.js → chunk-MHA7Q3KU.js} +248 -102
- package/dist/chunk-MHA7Q3KU.js.map +1 -0
- package/dist/{chunk-TZYINKCC.js → chunk-MQTVMB7W.js} +33 -28
- package/dist/chunk-MQTVMB7W.js.map +1 -0
- package/dist/chunk-NP47HRX2.js +136 -0
- package/dist/chunk-NP47HRX2.js.map +1 -0
- package/dist/{chunk-EG2MGDP5.js → chunk-NYDAHE3Y.js} +52 -46
- package/dist/chunk-NYDAHE3Y.js.map +1 -0
- package/dist/{chunk-45WZ6GMI.js → chunk-P7ZYOC23.js} +16 -16
- package/dist/{chunk-3R27MJ5R.js → chunk-PFWHHYEO.js} +3 -3
- package/dist/{chunk-NEWNXZ65.js → chunk-PHPAK2L6.js} +28 -26
- package/dist/{chunk-NEWNXZ65.js.map → chunk-PHPAK2L6.js.map} +1 -1
- package/dist/{chunk-PPZ3K4YM.js → chunk-PRGPSNGA.js} +2 -2
- package/dist/{chunk-V7QOIQYC.js → chunk-RGFMQFX7.js} +9 -23
- package/dist/{chunk-V7QOIQYC.js.map → chunk-RGFMQFX7.js.map} +1 -1
- package/dist/{chunk-YVPZUN6X.js → chunk-TJVX42KY.js} +335 -38
- package/dist/chunk-TJVX42KY.js.map +1 -0
- package/dist/{chunk-HOAHMERN.js → chunk-UDRAD7MF.js} +21 -21
- package/dist/{chunk-QF7VEL3M.js → chunk-UHDXXLYH.js} +39 -127
- package/dist/chunk-UHDXXLYH.js.map +1 -0
- package/dist/{chunk-RNFUVKC3.js → chunk-UPAAZU4W.js} +30 -13
- package/dist/chunk-UPAAZU4W.js.map +1 -0
- package/dist/{chunk-L6HSKTQE.js → chunk-W7LZLJAF.js} +4 -4
- package/dist/{chunk-KMCS76ZW.js → chunk-WB2FVIUS.js} +44 -12
- package/dist/chunk-WB2FVIUS.js.map +1 -0
- package/dist/{chunk-SA6HXL4Y.js → chunk-WPHC6JBR.js} +9 -9
- package/dist/chunk-WPHC6JBR.js.map +1 -0
- package/dist/{chunk-KRAGAUKD.js → chunk-XHCP6ZNO.js} +201 -64
- package/dist/chunk-XHCP6ZNO.js.map +1 -0
- package/dist/{chunk-7F7LGMVO.js → chunk-XKR3VD2Z.js} +12 -12
- package/dist/{chunk-7F7LGMVO.js.map → chunk-XKR3VD2Z.js.map} +1 -1
- package/dist/{chunk-ICM463EO.js → chunk-XNK5UN2D.js} +5 -649
- package/dist/chunk-XNK5UN2D.js.map +1 -0
- package/dist/{chunk-LTH6XYZ3.js → chunk-XQLNZW7I.js} +40 -30
- package/dist/chunk-XQLNZW7I.js.map +1 -0
- package/dist/{chunk-62GVVZQO.js → chunk-ZAYATXJD.js} +32 -316
- package/dist/chunk-ZAYATXJD.js.map +1 -0
- package/dist/cli.js +127 -59
- package/dist/cli.js.map +1 -1
- package/dist/compat-YPDZB34U.js +24 -0
- package/dist/{config-LEBZZCJP.js → config-56B4OHZE.js} +20 -17
- package/dist/{contract-JH2WBIO6.js → contract-HWLFFL3F.js} +2 -2
- package/dist/{conventions-XOT6YW5L.js → conventions-DW2CM5I3.js} +20 -16
- package/dist/{conventions-XOT6YW5L.js.map → conventions-DW2CM5I3.js.map} +1 -1
- package/dist/db-5NU77PJA.js +264 -0
- package/dist/db-5NU77PJA.js.map +1 -0
- package/dist/diagnostics-UCTS2VUF.js +25 -0
- package/dist/execute-R7AVOLNG.js +38 -0
- package/dist/{generate-JGW5JSKB.js → generate-O7GUXLN4.js} +46 -43
- package/dist/generate-O7GUXLN4.js.map +1 -0
- package/dist/{git-A2IP32PQ.js → git-LCG45YXD.js} +2 -3
- package/dist/history-6GSURU3H.js +101 -0
- package/dist/history-6GSURU3H.js.map +1 -0
- package/dist/init-DD6DBRC5.js +28 -0
- package/dist/{operations-O4FORFIH.js → operations-HU5ZFEBA.js} +78 -52
- package/dist/operations-HU5ZFEBA.js.map +1 -0
- package/dist/{permissions-G4XCAESF.js → permissions-NNNSRC4P.js} +5 -6
- package/dist/plan-XR6B7EM3.js +38 -0
- package/dist/{policy-U5NBFAO5.js → policy-RFJOJQL6.js} +17 -15
- package/dist/{policy-U5NBFAO5.js.map → policy-RFJOJQL6.js.map} +1 -1
- package/dist/pr-OSCNJCOO.js +42 -0
- package/dist/pr-review-IKIYHFBO.js +33 -0
- package/dist/prd-HFDZJJQR.js +37 -0
- package/dist/{ps-76L6ZAZL.js → ps-AKRHGPSR.js} +26 -15
- package/dist/ps-AKRHGPSR.js.map +1 -0
- package/dist/{recorder-2BAKPBRA.js → recorder-T7426CKV.js} +11 -7
- package/dist/registry-DWRKD65A.js +17 -0
- package/dist/repository-TABBRW2C.js +80 -0
- package/dist/resume-GDXKK5XP.js +278 -0
- package/dist/resume-GDXKK5XP.js.map +1 -0
- package/dist/review-JJHMQA2O.js +38 -0
- package/dist/routing-FPNUVE7T.js +35 -0
- package/dist/run-RLCURLLL.js +61 -0
- package/dist/{session-publisher-KAJQRHNZ.js → session-publisher-Q5TNEZPX.js} +3 -3
- package/dist/session-publisher-Q5TNEZPX.js.map +1 -0
- package/dist/{state-manager-UPHO2EA2.js → state-manager-IGNTD4AB.js} +9 -4
- package/dist/state-manager-IGNTD4AB.js.map +1 -0
- package/dist/{usage-KTMYXAPF.js → usage-XW27AL5K.js} +69 -63
- package/dist/usage-XW27AL5K.js.map +1 -0
- package/dist/web-NCDBVZ5Y.js +205 -0
- package/dist/web-NCDBVZ5Y.js.map +1 -0
- package/package.json +20 -7
- package/prompts/analyze.md +16 -19
- package/prompts/execute.md +73 -144
- package/prompts/generate.md +23 -10
- package/prompts/plan.md +32 -60
- package/prompts/pr-review.md +58 -40
- package/prompts/pr.md +50 -16
- package/prompts/prd.md +26 -19
- package/prompts/review.md +41 -18
- package/web/public/app.js +109 -110
- package/dist/agent-HMZTXCRT.js +0 -30
- package/dist/analyze-Q4ONIWRZ.js.map +0 -1
- package/dist/apply-EEFPY2W4.js.map +0 -1
- package/dist/chunk-34OCSKOB.js.map +0 -1
- package/dist/chunk-62GVVZQO.js.map +0 -1
- package/dist/chunk-6K2LCFG6.js.map +0 -1
- package/dist/chunk-7UMMFOL2.js.map +0 -1
- package/dist/chunk-CRGIQFUQ.js.map +0 -1
- package/dist/chunk-CWSQVHVV.js.map +0 -1
- package/dist/chunk-E4I6MMHD.js.map +0 -1
- package/dist/chunk-ECU7YO7S.js.map +0 -1
- package/dist/chunk-EG2MGDP5.js.map +0 -1
- package/dist/chunk-HAKDLR6N.js.map +0 -1
- package/dist/chunk-ICM463EO.js.map +0 -1
- package/dist/chunk-KMCS76ZW.js.map +0 -1
- package/dist/chunk-KRAGAUKD.js.map +0 -1
- package/dist/chunk-LGHND47K.js.map +0 -1
- package/dist/chunk-LI35PIIS.js.map +0 -1
- package/dist/chunk-LTH6XYZ3.js.map +0 -1
- package/dist/chunk-PMLE7KAQ.js.map +0 -1
- package/dist/chunk-QF7VEL3M.js.map +0 -1
- package/dist/chunk-RNFUVKC3.js.map +0 -1
- package/dist/chunk-SA6HXL4Y.js.map +0 -1
- package/dist/chunk-SOM4QA6C.js.map +0 -1
- package/dist/chunk-TZYINKCC.js.map +0 -1
- package/dist/chunk-WCCZJL7W.js +0 -141
- package/dist/chunk-WCCZJL7W.js.map +0 -1
- package/dist/chunk-XEYH6ZZI.js +0 -289
- package/dist/chunk-XEYH6ZZI.js.map +0 -1
- package/dist/chunk-XUEMATVY.js.map +0 -1
- package/dist/chunk-XZUEFTUP.js +0 -243
- package/dist/chunk-XZUEFTUP.js.map +0 -1
- package/dist/chunk-YVPZUN6X.js.map +0 -1
- package/dist/diagnostics-HTCWF7F4.js +0 -23
- package/dist/execute-LELOSR5T.js +0 -37
- package/dist/generate-JGW5JSKB.js.map +0 -1
- package/dist/init-TO6MUXO4.js +0 -26
- package/dist/operations-O4FORFIH.js.map +0 -1
- package/dist/plan-OXAT344Q.js +0 -34
- package/dist/pr-UZHNKKZC.js +0 -39
- package/dist/pr-review-Q4ACAWDM.js +0 -32
- package/dist/prd-6CYEMLDY.js +0 -33
- package/dist/ps-76L6ZAZL.js.map +0 -1
- package/dist/registry-XLHHYLTR.js +0 -15
- package/dist/resume-URCU6CIJ.js +0 -244
- package/dist/resume-URCU6CIJ.js.map +0 -1
- package/dist/review-LVZT2TMW.js +0 -35
- package/dist/routing-EK6RBZM4.js +0 -33
- package/dist/run-2RXJCJKT.js +0 -56
- package/dist/usage-KTMYXAPF.js.map +0 -1
- package/dist/web-B5UPHHR5.js +0 -172
- package/dist/web-B5UPHHR5.js.map +0 -1
- /package/dist/{agent-HMZTXCRT.js.map → agent-EWKA7357.js.map} +0 -0
- /package/dist/{bench-WWWXLTFH.js.map → bench-ZJEIACUK.js.map} +0 -0
- /package/dist/{chunk-RYOUZ6EK.js.map → chunk-23VHVVKW.js.map} +0 -0
- /package/dist/{chunk-C5TL7T4V.js.map → chunk-2YM2VH6R.js.map} +0 -0
- /package/dist/{chunk-QPGEFLMY.js.map → chunk-7CMVL4X6.js.map} +0 -0
- /package/dist/{chunk-DUS5ZNKL.js.map → chunk-DIMDAYZS.js.map} +0 -0
- /package/dist/{chunk-DGRTJD5R.js.map → chunk-DXH4TQRB.js.map} +0 -0
- /package/dist/{chunk-YUNBOUQH.js.map → chunk-ERAG5IQB.js.map} +0 -0
- /package/dist/{chunk-ZVZMWNCG.js.map → chunk-F2NFJ27Z.js.map} +0 -0
- /package/dist/{chunk-QJ44VG5G.js.map → chunk-KDQRNVBZ.js.map} +0 -0
- /package/dist/{chunk-45WZ6GMI.js.map → chunk-P7ZYOC23.js.map} +0 -0
- /package/dist/{chunk-3R27MJ5R.js.map → chunk-PFWHHYEO.js.map} +0 -0
- /package/dist/{chunk-PPZ3K4YM.js.map → chunk-PRGPSNGA.js.map} +0 -0
- /package/dist/{chunk-HOAHMERN.js.map → chunk-UDRAD7MF.js.map} +0 -0
- /package/dist/{chunk-L6HSKTQE.js.map → chunk-W7LZLJAF.js.map} +0 -0
- /package/dist/{config-LEBZZCJP.js.map → compat-YPDZB34U.js.map} +0 -0
- /package/dist/{contract-JH2WBIO6.js.map → config-56B4OHZE.js.map} +0 -0
- /package/dist/{diagnostics-HTCWF7F4.js.map → contract-HWLFFL3F.js.map} +0 -0
- /package/dist/{execute-LELOSR5T.js.map → diagnostics-UCTS2VUF.js.map} +0 -0
- /package/dist/{git-A2IP32PQ.js.map → execute-R7AVOLNG.js.map} +0 -0
- /package/dist/{init-TO6MUXO4.js.map → git-LCG45YXD.js.map} +0 -0
- /package/dist/{permissions-G4XCAESF.js.map → init-DD6DBRC5.js.map} +0 -0
- /package/dist/{plan-OXAT344Q.js.map → permissions-NNNSRC4P.js.map} +0 -0
- /package/dist/{pr-UZHNKKZC.js.map → plan-XR6B7EM3.js.map} +0 -0
- /package/dist/{pr-review-Q4ACAWDM.js.map → pr-OSCNJCOO.js.map} +0 -0
- /package/dist/{prd-6CYEMLDY.js.map → pr-review-IKIYHFBO.js.map} +0 -0
- /package/dist/{recorder-2BAKPBRA.js.map → prd-HFDZJJQR.js.map} +0 -0
- /package/dist/{registry-XLHHYLTR.js.map → recorder-T7426CKV.js.map} +0 -0
- /package/dist/{review-LVZT2TMW.js.map → registry-DWRKD65A.js.map} +0 -0
- /package/dist/{routing-EK6RBZM4.js.map → repository-TABBRW2C.js.map} +0 -0
- /package/dist/{run-2RXJCJKT.js.map → review-JJHMQA2O.js.map} +0 -0
- /package/dist/{session-publisher-KAJQRHNZ.js.map → routing-FPNUVE7T.js.map} +0 -0
- /package/dist/{state-manager-UPHO2EA2.js.map → run-RLCURLLL.js.map} +0 -0
|
@@ -0,0 +1,1571 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
taskPlanSchema
|
|
4
|
+
} from "./chunk-XNK5UN2D.js";
|
|
5
|
+
import {
|
|
6
|
+
getGlobalRoot
|
|
7
|
+
} from "./chunk-MAJ6QDRL.js";
|
|
8
|
+
import {
|
|
9
|
+
writeFileAtomic
|
|
10
|
+
} from "./chunk-PFWHHYEO.js";
|
|
11
|
+
|
|
12
|
+
// src/storage/db/repository.ts
|
|
13
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
14
|
+
import { mkdir, readFile } from "node:fs/promises";
|
|
15
|
+
import { basename, dirname as dirname2 } from "node:path";
|
|
16
|
+
|
|
17
|
+
// src/storage/db/index.ts
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
|
|
20
|
+
// src/storage/db/driver.ts
|
|
21
|
+
import { mkdirSync, statfsSync } from "node:fs";
|
|
22
|
+
import { dirname } from "node:path";
|
|
23
|
+
var NETWORK_FILESYSTEM_TYPES = /* @__PURE__ */ new Set([26985, 20859, 4283649346]);
|
|
24
|
+
function isNetworkFilesystem(path) {
|
|
25
|
+
try {
|
|
26
|
+
return NETWORK_FILESYSTEM_TYPES.has(Number(statfsSync(dirname(path)).type));
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function isSqliteExperimentalWarning(warning) {
|
|
32
|
+
if (!(warning instanceof Error) || warning.name !== "ExperimentalWarning") return false;
|
|
33
|
+
return warning.message.includes("SQLite is an experimental feature");
|
|
34
|
+
}
|
|
35
|
+
async function loadDatabaseSync() {
|
|
36
|
+
const originalEmitWarning = process.emitWarning;
|
|
37
|
+
process.emitWarning = ((warning, ...args) => {
|
|
38
|
+
const normalized = typeof warning === "string" ? Object.assign(new Error(warning), {
|
|
39
|
+
name: typeof args[0] === "string" ? args[0] : "Warning"
|
|
40
|
+
}) : warning;
|
|
41
|
+
if (isSqliteExperimentalWarning(normalized)) return;
|
|
42
|
+
return originalEmitWarning(warning, ...args);
|
|
43
|
+
});
|
|
44
|
+
try {
|
|
45
|
+
const sqlite = await import("node:sqlite");
|
|
46
|
+
return sqlite.DatabaseSync;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
49
|
+
throw new Error(
|
|
50
|
+
`SQLite storage requires Node.js >= 22.13.0 with the built-in node:sqlite module. ${detail}`,
|
|
51
|
+
{ cause: error }
|
|
52
|
+
);
|
|
53
|
+
} finally {
|
|
54
|
+
process.emitWarning = originalEmitWarning;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
var NodeSqliteDriver = class {
|
|
58
|
+
constructor(database) {
|
|
59
|
+
this.database = database;
|
|
60
|
+
}
|
|
61
|
+
database;
|
|
62
|
+
exec(sql) {
|
|
63
|
+
this.database.exec(sql);
|
|
64
|
+
}
|
|
65
|
+
prepare(sql) {
|
|
66
|
+
return this.database.prepare(sql);
|
|
67
|
+
}
|
|
68
|
+
transaction(work) {
|
|
69
|
+
this.exec("BEGIN IMMEDIATE");
|
|
70
|
+
try {
|
|
71
|
+
const value = work();
|
|
72
|
+
this.exec("COMMIT");
|
|
73
|
+
return value;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
try {
|
|
76
|
+
this.exec("ROLLBACK");
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
integrityCheck() {
|
|
83
|
+
return this.prepare("PRAGMA integrity_check").get()?.integrity_check ?? "unknown";
|
|
84
|
+
}
|
|
85
|
+
backup(destination) {
|
|
86
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
87
|
+
this.prepare("VACUUM INTO ?").run(destination);
|
|
88
|
+
}
|
|
89
|
+
vacuum() {
|
|
90
|
+
this.exec("VACUUM");
|
|
91
|
+
}
|
|
92
|
+
close() {
|
|
93
|
+
this.database.close();
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
async function openDatabase(path, options = {}) {
|
|
97
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
98
|
+
const DatabaseSync = await loadDatabaseSync();
|
|
99
|
+
const driver = new NodeSqliteDriver(new DatabaseSync(path));
|
|
100
|
+
try {
|
|
101
|
+
driver.exec("PRAGMA foreign_keys = ON");
|
|
102
|
+
driver.exec("PRAGMA busy_timeout = 5000");
|
|
103
|
+
driver.exec("PRAGMA synchronous = NORMAL");
|
|
104
|
+
const networkFilesystem = (options.isNetworkFilesystem ?? isNetworkFilesystem)(path);
|
|
105
|
+
driver.exec(`PRAGMA journal_mode = ${networkFilesystem ? "DELETE" : "WAL"}`);
|
|
106
|
+
if (networkFilesystem) {
|
|
107
|
+
options.onWarning?.(
|
|
108
|
+
`SQLite database is on a network filesystem; using journal_mode=DELETE instead of WAL: ${path}`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return driver;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
driver.close();
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/storage/db/migrations.ts
|
|
119
|
+
var INITIAL_SCHEMA = `
|
|
120
|
+
CREATE TABLE projects (
|
|
121
|
+
id TEXT PRIMARY KEY,
|
|
122
|
+
root TEXT NOT NULL,
|
|
123
|
+
remote_url TEXT,
|
|
124
|
+
created_at TEXT NOT NULL,
|
|
125
|
+
updated_at TEXT NOT NULL
|
|
126
|
+
);
|
|
127
|
+
CREATE TABLE issues (
|
|
128
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
129
|
+
id TEXT NOT NULL,
|
|
130
|
+
title TEXT,
|
|
131
|
+
status TEXT NOT NULL,
|
|
132
|
+
branch_name TEXT,
|
|
133
|
+
created_at TEXT NOT NULL,
|
|
134
|
+
updated_at TEXT NOT NULL,
|
|
135
|
+
PRIMARY KEY (project_id, id)
|
|
136
|
+
);
|
|
137
|
+
CREATE TABLE pipelines (
|
|
138
|
+
project_id TEXT NOT NULL,
|
|
139
|
+
issue_id TEXT NOT NULL,
|
|
140
|
+
state_json TEXT NOT NULL,
|
|
141
|
+
updated_at TEXT NOT NULL,
|
|
142
|
+
PRIMARY KEY (project_id, issue_id),
|
|
143
|
+
FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE
|
|
144
|
+
);
|
|
145
|
+
CREATE TABLE stories (
|
|
146
|
+
project_id TEXT NOT NULL,
|
|
147
|
+
issue_id TEXT NOT NULL,
|
|
148
|
+
id TEXT NOT NULL,
|
|
149
|
+
title TEXT NOT NULL,
|
|
150
|
+
priority INTEGER NOT NULL,
|
|
151
|
+
passes INTEGER NOT NULL CHECK (passes IN (0, 1)),
|
|
152
|
+
notes TEXT NOT NULL DEFAULT '',
|
|
153
|
+
PRIMARY KEY (project_id, issue_id, id),
|
|
154
|
+
FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE
|
|
155
|
+
);
|
|
156
|
+
CREATE TABLE story_dependencies (
|
|
157
|
+
project_id TEXT NOT NULL,
|
|
158
|
+
issue_id TEXT NOT NULL,
|
|
159
|
+
story_id TEXT NOT NULL,
|
|
160
|
+
depends_on_story_id TEXT NOT NULL,
|
|
161
|
+
PRIMARY KEY (project_id, issue_id, story_id, depends_on_story_id),
|
|
162
|
+
FOREIGN KEY (project_id, issue_id, story_id) REFERENCES stories(project_id, issue_id, id) ON DELETE CASCADE,
|
|
163
|
+
FOREIGN KEY (project_id, issue_id, depends_on_story_id) REFERENCES stories(project_id, issue_id, id) ON DELETE CASCADE,
|
|
164
|
+
CHECK (story_id <> depends_on_story_id)
|
|
165
|
+
);
|
|
166
|
+
CREATE TABLE runs (
|
|
167
|
+
id TEXT PRIMARY KEY,
|
|
168
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
169
|
+
issue_id TEXT,
|
|
170
|
+
status TEXT NOT NULL,
|
|
171
|
+
started_at TEXT NOT NULL,
|
|
172
|
+
finished_at TEXT,
|
|
173
|
+
FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE
|
|
174
|
+
);
|
|
175
|
+
CREATE TABLE phases (
|
|
176
|
+
id TEXT PRIMARY KEY,
|
|
177
|
+
run_id TEXT NOT NULL REFERENCES runs(id) ON DELETE CASCADE,
|
|
178
|
+
name TEXT NOT NULL,
|
|
179
|
+
status TEXT NOT NULL,
|
|
180
|
+
started_at TEXT NOT NULL,
|
|
181
|
+
finished_at TEXT
|
|
182
|
+
);
|
|
183
|
+
CREATE TABLE executions (
|
|
184
|
+
id TEXT PRIMARY KEY,
|
|
185
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
186
|
+
issue_id TEXT,
|
|
187
|
+
run_id TEXT REFERENCES runs(id) ON DELETE SET NULL,
|
|
188
|
+
phase_id TEXT REFERENCES phases(id) ON DELETE SET NULL,
|
|
189
|
+
status TEXT NOT NULL,
|
|
190
|
+
started_at TEXT NOT NULL,
|
|
191
|
+
finished_at TEXT,
|
|
192
|
+
duration_ms INTEGER,
|
|
193
|
+
cost_status TEXT NOT NULL CHECK (cost_status IN ('reported', 'estimated', 'unknown')),
|
|
194
|
+
cost_amount REAL,
|
|
195
|
+
CHECK ((cost_status = 'unknown' AND cost_amount IS NULL) OR (cost_status IN ('reported', 'estimated') AND cost_amount IS NOT NULL AND cost_amount >= 0)),
|
|
196
|
+
FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE
|
|
197
|
+
);
|
|
198
|
+
CREATE TABLE events (
|
|
199
|
+
id TEXT PRIMARY KEY,
|
|
200
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
201
|
+
run_id TEXT REFERENCES runs(id) ON DELETE CASCADE,
|
|
202
|
+
occurred_at TEXT NOT NULL,
|
|
203
|
+
kind TEXT NOT NULL,
|
|
204
|
+
payload_json TEXT NOT NULL
|
|
205
|
+
);
|
|
206
|
+
CREATE TABLE snapshots (
|
|
207
|
+
id TEXT PRIMARY KEY,
|
|
208
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
209
|
+
run_id TEXT REFERENCES runs(id) ON DELETE CASCADE,
|
|
210
|
+
created_at TEXT NOT NULL,
|
|
211
|
+
payload_json TEXT NOT NULL
|
|
212
|
+
);
|
|
213
|
+
CREATE TABLE pull_requests (
|
|
214
|
+
id TEXT PRIMARY KEY,
|
|
215
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
216
|
+
issue_id TEXT,
|
|
217
|
+
number INTEGER,
|
|
218
|
+
url TEXT,
|
|
219
|
+
status TEXT NOT NULL,
|
|
220
|
+
created_at TEXT NOT NULL,
|
|
221
|
+
FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE
|
|
222
|
+
);
|
|
223
|
+
CREATE TABLE reviews (
|
|
224
|
+
id TEXT PRIMARY KEY,
|
|
225
|
+
pull_request_id TEXT NOT NULL REFERENCES pull_requests(id) ON DELETE CASCADE,
|
|
226
|
+
status TEXT NOT NULL,
|
|
227
|
+
created_at TEXT NOT NULL,
|
|
228
|
+
payload_json TEXT NOT NULL
|
|
229
|
+
);
|
|
230
|
+
CREATE TABLE verifications (
|
|
231
|
+
id TEXT PRIMARY KEY,
|
|
232
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
233
|
+
issue_id TEXT,
|
|
234
|
+
status TEXT NOT NULL,
|
|
235
|
+
created_at TEXT NOT NULL,
|
|
236
|
+
payload_json TEXT NOT NULL,
|
|
237
|
+
FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE
|
|
238
|
+
);
|
|
239
|
+
CREATE TABLE provider_health (
|
|
240
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
241
|
+
provider TEXT NOT NULL,
|
|
242
|
+
payload_json TEXT NOT NULL,
|
|
243
|
+
updated_at TEXT NOT NULL,
|
|
244
|
+
PRIMARY KEY (project_id, provider)
|
|
245
|
+
);
|
|
246
|
+
CREATE TABLE queues (
|
|
247
|
+
id TEXT PRIMARY KEY,
|
|
248
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
249
|
+
status TEXT NOT NULL,
|
|
250
|
+
payload_json TEXT NOT NULL,
|
|
251
|
+
created_at TEXT NOT NULL,
|
|
252
|
+
updated_at TEXT NOT NULL
|
|
253
|
+
);
|
|
254
|
+
CREATE TABLE queue_issues (
|
|
255
|
+
queue_id TEXT NOT NULL REFERENCES queues(id) ON DELETE CASCADE,
|
|
256
|
+
project_id TEXT NOT NULL,
|
|
257
|
+
issue_id TEXT NOT NULL,
|
|
258
|
+
position INTEGER NOT NULL,
|
|
259
|
+
status TEXT NOT NULL,
|
|
260
|
+
PRIMARY KEY (queue_id, project_id, issue_id),
|
|
261
|
+
UNIQUE (queue_id, position),
|
|
262
|
+
FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE
|
|
263
|
+
);
|
|
264
|
+
CREATE TABLE migrated_artifacts (
|
|
265
|
+
source_path TEXT PRIMARY KEY,
|
|
266
|
+
sha256 TEXT NOT NULL,
|
|
267
|
+
migrated_at TEXT NOT NULL,
|
|
268
|
+
table_counts_json TEXT NOT NULL
|
|
269
|
+
);
|
|
270
|
+
CREATE TABLE audit_log (
|
|
271
|
+
id TEXT PRIMARY KEY,
|
|
272
|
+
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
273
|
+
occurred_at TEXT NOT NULL,
|
|
274
|
+
action TEXT NOT NULL,
|
|
275
|
+
payload_json TEXT NOT NULL
|
|
276
|
+
);
|
|
277
|
+
CREATE INDEX executions_project_issue_started_idx ON executions(project_id, issue_id, started_at);
|
|
278
|
+
CREATE INDEX runs_project_status_started_idx ON runs(project_id, status, started_at);
|
|
279
|
+
CREATE INDEX stories_project_issue_priority_idx ON stories(project_id, issue_id, priority);
|
|
280
|
+
CREATE INDEX phases_run_name_idx ON phases(run_id, name);
|
|
281
|
+
CREATE INDEX events_project_run_occurred_idx ON events(project_id, run_id, occurred_at);
|
|
282
|
+
`;
|
|
283
|
+
var migrations = [
|
|
284
|
+
{
|
|
285
|
+
version: 1,
|
|
286
|
+
name: "initial relational storage",
|
|
287
|
+
up: (database) => database.exec(INITIAL_SCHEMA)
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
version: 2,
|
|
291
|
+
name: "preserve imported execution details",
|
|
292
|
+
up: (database) => database.exec("ALTER TABLE executions ADD COLUMN payload_json TEXT NOT NULL DEFAULT '{}'")
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
version: 3,
|
|
296
|
+
name: "index numeric user story identifiers",
|
|
297
|
+
up: (database) => database.exec(`
|
|
298
|
+
ALTER TABLE stories ADD COLUMN story_number INTEGER;
|
|
299
|
+
CREATE INDEX stories_project_number_idx ON stories(project_id, story_number DESC);
|
|
300
|
+
`)
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
version: 4,
|
|
304
|
+
name: "complete relational state and history indexes",
|
|
305
|
+
up: (database) => database.exec(`
|
|
306
|
+
ALTER TABLE pipelines ADD COLUMN project TEXT;
|
|
307
|
+
ALTER TABLE pipelines ADD COLUMN issue_number TEXT;
|
|
308
|
+
ALTER TABLE pipelines ADD COLUMN issue_url TEXT;
|
|
309
|
+
ALTER TABLE pipelines ADD COLUMN branch_name TEXT;
|
|
310
|
+
ALTER TABLE pipelines ADD COLUMN no_branch INTEGER NOT NULL DEFAULT 0 CHECK (no_branch IN (0, 1));
|
|
311
|
+
ALTER TABLE pipelines ADD COLUMN description TEXT;
|
|
312
|
+
ALTER TABLE pipelines ADD COLUMN issue_status TEXT;
|
|
313
|
+
ALTER TABLE pipelines ADD COLUMN completed_at TEXT;
|
|
314
|
+
ALTER TABLE pipelines ADD COLUMN last_attempt_at TEXT;
|
|
315
|
+
ALTER TABLE pipelines ADD COLUMN last_error_category TEXT;
|
|
316
|
+
ALTER TABLE pipelines ADD COLUMN last_error_message TEXT;
|
|
317
|
+
ALTER TABLE pipelines ADD COLUMN last_error_at TEXT;
|
|
318
|
+
ALTER TABLE pipelines ADD COLUMN correction_cycle INTEGER NOT NULL DEFAULT 0;
|
|
319
|
+
ALTER TABLE pipelines ADD COLUMN max_correction_cycles INTEGER NOT NULL DEFAULT 3;
|
|
320
|
+
ALTER TABLE pipelines ADD COLUMN last_review_findings TEXT;
|
|
321
|
+
ALTER TABLE pipelines ADD COLUMN analyze_completed INTEGER CHECK (analyze_completed IN (0, 1));
|
|
322
|
+
ALTER TABLE pipelines ADD COLUMN prd_completed INTEGER CHECK (prd_completed IN (0, 1));
|
|
323
|
+
ALTER TABLE pipelines ADD COLUMN json_completed INTEGER CHECK (json_completed IN (0, 1));
|
|
324
|
+
ALTER TABLE pipelines ADD COLUMN execution_completed INTEGER CHECK (execution_completed IN (0, 1));
|
|
325
|
+
ALTER TABLE pipelines ADD COLUMN review_completed INTEGER CHECK (review_completed IN (0, 1));
|
|
326
|
+
ALTER TABLE pipelines ADD COLUMN pr_created INTEGER CHECK (pr_created IN (0, 1));
|
|
327
|
+
ALTER TABLE pipelines ADD COLUMN pr_review_completed INTEGER CHECK (pr_review_completed IN (0, 1));
|
|
328
|
+
ALTER TABLE pipelines ADD COLUMN run_status TEXT;
|
|
329
|
+
ALTER TABLE pipelines ADD COLUMN run_phase TEXT;
|
|
330
|
+
ALTER TABLE pipelines ADD COLUMN run_attempt INTEGER;
|
|
331
|
+
ALTER TABLE pipelines ADD COLUMN run_heartbeat_at TEXT;
|
|
332
|
+
ALTER TABLE pipelines ADD COLUMN run_blocked_reason TEXT;
|
|
333
|
+
ALTER TABLE pipelines ADD COLUMN run_owner_pid INTEGER;
|
|
334
|
+
ALTER TABLE pipelines ADD COLUMN run_owner_host TEXT;
|
|
335
|
+
ALTER TABLE pipelines ADD COLUMN run_owner_started_at TEXT;
|
|
336
|
+
ALTER TABLE pipelines ADD COLUMN pr_number INTEGER;
|
|
337
|
+
ALTER TABLE pipelines ADD COLUMN pr_url TEXT;
|
|
338
|
+
ALTER TABLE pipelines ADD COLUMN pr_head_branch TEXT;
|
|
339
|
+
ALTER TABLE pipelines ADD COLUMN pr_created_at TEXT;
|
|
340
|
+
ALTER TABLE pipelines ADD COLUMN pr_review_enabled INTEGER CHECK (pr_review_enabled IN (0, 1));
|
|
341
|
+
ALTER TABLE pipelines ADD COLUMN pr_review_rounds INTEGER;
|
|
342
|
+
ALTER TABLE pipelines ADD COLUMN pr_review_recommendation TEXT;
|
|
343
|
+
ALTER TABLE pipelines ADD COLUMN pr_reviewed_at TEXT;
|
|
344
|
+
|
|
345
|
+
ALTER TABLE stories ADD COLUMN description TEXT;
|
|
346
|
+
ALTER TABLE stories ADD COLUMN acceptance_criteria_json TEXT NOT NULL DEFAULT '[]';
|
|
347
|
+
ALTER TABLE stories ADD COLUMN duration_seconds REAL;
|
|
348
|
+
ALTER TABLE stories ADD COLUMN status TEXT;
|
|
349
|
+
ALTER TABLE stories ADD COLUMN stage TEXT;
|
|
350
|
+
ALTER TABLE stories ADD COLUMN stage_since TEXT;
|
|
351
|
+
ALTER TABLE stories ADD COLUMN stage_detail TEXT;
|
|
352
|
+
ALTER TABLE stories ADD COLUMN input_tokens INTEGER;
|
|
353
|
+
ALTER TABLE stories ADD COLUMN output_tokens INTEGER;
|
|
354
|
+
ALTER TABLE stories ADD COLUMN cache_read_tokens INTEGER;
|
|
355
|
+
ALTER TABLE stories ADD COLUMN cache_creation_tokens INTEGER;
|
|
356
|
+
|
|
357
|
+
ALTER TABLE runs ADD COLUMN session_id TEXT;
|
|
358
|
+
ALTER TABLE runs ADD COLUMN heartbeat_at TEXT;
|
|
359
|
+
ALTER TABLE runs ADD COLUMN pid INTEGER;
|
|
360
|
+
ALTER TABLE runs ADD COLUMN host TEXT;
|
|
361
|
+
ALTER TABLE events ADD COLUMN session_id TEXT;
|
|
362
|
+
ALTER TABLE events ADD COLUMN sequence INTEGER;
|
|
363
|
+
ALTER TABLE snapshots ADD COLUMN issue_id TEXT;
|
|
364
|
+
ALTER TABLE snapshots ADD COLUMN session_id TEXT;
|
|
365
|
+
ALTER TABLE snapshots ADD COLUMN updated_at TEXT;
|
|
366
|
+
ALTER TABLE executions ADD COLUMN session_id TEXT;
|
|
367
|
+
ALTER TABLE executions ADD COLUMN purpose TEXT;
|
|
368
|
+
ALTER TABLE executions ADD COLUMN attempt INTEGER;
|
|
369
|
+
ALTER TABLE executions ADD COLUMN trigger TEXT;
|
|
370
|
+
ALTER TABLE executions ADD COLUMN trigger_reason TEXT;
|
|
371
|
+
ALTER TABLE executions ADD COLUMN input_tokens INTEGER;
|
|
372
|
+
ALTER TABLE executions ADD COLUMN output_tokens INTEGER;
|
|
373
|
+
ALTER TABLE executions ADD COLUMN cache_read_tokens INTEGER;
|
|
374
|
+
ALTER TABLE executions ADD COLUMN cache_creation_tokens INTEGER;
|
|
375
|
+
ALTER TABLE executions ADD COLUMN reasoning_tokens INTEGER;
|
|
376
|
+
ALTER TABLE executions ADD COLUMN harness TEXT;
|
|
377
|
+
ALTER TABLE executions ADD COLUMN provider TEXT;
|
|
378
|
+
ALTER TABLE executions ADD COLUMN model_requested TEXT;
|
|
379
|
+
ALTER TABLE executions ADD COLUMN model_resolved TEXT;
|
|
380
|
+
|
|
381
|
+
ALTER TABLE provider_health ADD COLUMN status TEXT;
|
|
382
|
+
ALTER TABLE provider_health ADD COLUMN consecutive_failures INTEGER NOT NULL DEFAULT 0;
|
|
383
|
+
ALTER TABLE provider_health ADD COLUMN cooldown_level INTEGER NOT NULL DEFAULT 0;
|
|
384
|
+
ALTER TABLE provider_health ADD COLUMN cooldown_until TEXT;
|
|
385
|
+
ALTER TABLE provider_health ADD COLUMN last_failure_kind TEXT;
|
|
386
|
+
ALTER TABLE provider_health ADD COLUMN last_failure_at TEXT;
|
|
387
|
+
ALTER TABLE provider_health ADD COLUMN last_success_at TEXT;
|
|
388
|
+
ALTER TABLE provider_health ADD COLUMN probe_in_flight INTEGER NOT NULL DEFAULT 0 CHECK (probe_in_flight IN (0, 1));
|
|
389
|
+
ALTER TABLE provider_health ADD COLUMN probe_started_at TEXT;
|
|
390
|
+
CREATE TABLE provider_health_failures (
|
|
391
|
+
project_id TEXT NOT NULL,
|
|
392
|
+
provider TEXT NOT NULL,
|
|
393
|
+
occurred_at TEXT NOT NULL,
|
|
394
|
+
kind TEXT NOT NULL,
|
|
395
|
+
PRIMARY KEY (project_id, provider, occurred_at, kind),
|
|
396
|
+
FOREIGN KEY (project_id, provider) REFERENCES provider_health(project_id, provider) ON DELETE CASCADE
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
ALTER TABLE queues ADD COLUMN requested_json TEXT NOT NULL DEFAULT '[]';
|
|
400
|
+
ALTER TABLE queues ADD COLUMN branch_name TEXT;
|
|
401
|
+
ALTER TABLE queues ADD COLUMN no_branch INTEGER NOT NULL DEFAULT 0 CHECK (no_branch IN (0, 1));
|
|
402
|
+
ALTER TABLE queues ADD COLUMN pr_review INTEGER NOT NULL DEFAULT 0 CHECK (pr_review IN (0, 1));
|
|
403
|
+
ALTER TABLE queues ADD COLUMN truncated INTEGER NOT NULL DEFAULT 0 CHECK (truncated IN (0, 1));
|
|
404
|
+
ALTER TABLE queue_issues ADD COLUMN number INTEGER;
|
|
405
|
+
ALTER TABLE queue_issues ADD COLUMN title TEXT;
|
|
406
|
+
ALTER TABLE queue_issues ADD COLUMN url TEXT;
|
|
407
|
+
ALTER TABLE queue_issues ADD COLUMN source TEXT;
|
|
408
|
+
ALTER TABLE queue_issues ADD COLUMN origin TEXT;
|
|
409
|
+
ALTER TABLE queue_issues ADD COLUMN role TEXT;
|
|
410
|
+
ALTER TABLE queue_issues ADD COLUMN priority TEXT;
|
|
411
|
+
ALTER TABLE queue_issues ADD COLUMN heuristic INTEGER NOT NULL DEFAULT 0 CHECK (heuristic IN (0, 1));
|
|
412
|
+
ALTER TABLE queue_issues ADD COLUMN failed_phase TEXT;
|
|
413
|
+
ALTER TABLE queue_issues ADD COLUMN last_error_category TEXT;
|
|
414
|
+
ALTER TABLE queue_issues ADD COLUMN last_error_message TEXT;
|
|
415
|
+
ALTER TABLE queue_issues ADD COLUMN last_error_at TEXT;
|
|
416
|
+
ALTER TABLE queue_issues ADD COLUMN attempts INTEGER NOT NULL DEFAULT 0;
|
|
417
|
+
ALTER TABLE queue_issues ADD COLUMN blocked_reason TEXT;
|
|
418
|
+
ALTER TABLE queue_issues ADD COLUMN started_at TEXT;
|
|
419
|
+
ALTER TABLE queue_issues ADD COLUMN completed_at TEXT;
|
|
420
|
+
CREATE TABLE queue_dependencies (
|
|
421
|
+
queue_id TEXT NOT NULL REFERENCES queues(id) ON DELETE CASCADE,
|
|
422
|
+
issue_id TEXT NOT NULL,
|
|
423
|
+
depends_on_issue_id TEXT NOT NULL,
|
|
424
|
+
PRIMARY KEY (queue_id, issue_id, depends_on_issue_id),
|
|
425
|
+
CHECK (issue_id <> depends_on_issue_id)
|
|
426
|
+
);
|
|
427
|
+
CREATE TABLE user_story_numbering (
|
|
428
|
+
project_id TEXT PRIMARY KEY REFERENCES projects(id) ON DELETE CASCADE,
|
|
429
|
+
next_number INTEGER NOT NULL CHECK (next_number > 0),
|
|
430
|
+
source TEXT NOT NULL,
|
|
431
|
+
issue_id TEXT NOT NULL,
|
|
432
|
+
decided_at TEXT NOT NULL,
|
|
433
|
+
detail TEXT
|
|
434
|
+
);
|
|
435
|
+
CREATE UNIQUE INDEX events_run_sequence_idx ON events(run_id, sequence) WHERE run_id IS NOT NULL AND sequence IS NOT NULL;
|
|
436
|
+
CREATE INDEX snapshots_project_session_updated_idx ON snapshots(project_id, session_id, updated_at DESC);
|
|
437
|
+
CREATE INDEX events_project_session_sequence_idx ON events(project_id, session_id, sequence);
|
|
438
|
+
CREATE INDEX executions_project_purpose_started_idx ON executions(project_id, purpose, started_at);
|
|
439
|
+
CREATE INDEX queue_issues_project_status_idx ON queue_issues(project_id, status, position);
|
|
440
|
+
CREATE INDEX provider_health_failures_lookup_idx ON provider_health_failures(project_id, provider, occurred_at DESC);
|
|
441
|
+
`)
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
version: 5,
|
|
445
|
+
name: "preserve pr review target state",
|
|
446
|
+
up: (database) => database.exec("ALTER TABLE pipelines ADD COLUMN pr_review_pull_request_number INTEGER;")
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
version: 6,
|
|
450
|
+
name: "record completed project adoption and runtime phase history",
|
|
451
|
+
up: (database) => database.exec(`
|
|
452
|
+
CREATE TABLE project_imports (
|
|
453
|
+
project_id TEXT PRIMARY KEY REFERENCES projects(id) ON DELETE CASCADE,
|
|
454
|
+
completed_at TEXT NOT NULL
|
|
455
|
+
);
|
|
456
|
+
ALTER TABLE phases ADD COLUMN duration_ms INTEGER;
|
|
457
|
+
ALTER TABLE phases ADD COLUMN input_tokens INTEGER;
|
|
458
|
+
ALTER TABLE phases ADD COLUMN output_tokens INTEGER;
|
|
459
|
+
ALTER TABLE phases ADD COLUMN cache_read_tokens INTEGER;
|
|
460
|
+
ALTER TABLE phases ADD COLUMN cache_creation_tokens INTEGER;
|
|
461
|
+
ALTER TABLE phases ADD COLUMN cost_status TEXT;
|
|
462
|
+
ALTER TABLE phases ADD COLUMN cost_amount REAL;
|
|
463
|
+
CREATE INDEX phases_run_status_started_idx ON phases(run_id, status, started_at);
|
|
464
|
+
CREATE INDEX reviews_pull_request_created_idx ON reviews(pull_request_id, created_at DESC);
|
|
465
|
+
`)
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
version: 7,
|
|
469
|
+
name: "index execution history by harness and run",
|
|
470
|
+
up: (database) => database.exec(`
|
|
471
|
+
CREATE INDEX executions_harness_started_idx ON executions(harness, started_at);
|
|
472
|
+
CREATE INDEX executions_run_id_idx ON executions(run_id);
|
|
473
|
+
`)
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
version: 8,
|
|
477
|
+
name: "explicit issue closure authorization",
|
|
478
|
+
up: (database) => database.exec(`
|
|
479
|
+
ALTER TABLE pipelines ADD COLUMN close_issue INTEGER CHECK (close_issue IN (0, 1));
|
|
480
|
+
ALTER TABLE pipelines ADD COLUMN issue_closed_at TEXT;
|
|
481
|
+
`)
|
|
482
|
+
}
|
|
483
|
+
];
|
|
484
|
+
var CURRENT_SCHEMA_VERSION = migrations.at(-1)?.version ?? 0;
|
|
485
|
+
function userVersion(database) {
|
|
486
|
+
return Number(
|
|
487
|
+
database.prepare("PRAGMA user_version").get()?.user_version ?? 0
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
function migrateDatabase(database) {
|
|
491
|
+
const current = userVersion(database);
|
|
492
|
+
if (current > CURRENT_SCHEMA_VERSION) {
|
|
493
|
+
throw new Error(
|
|
494
|
+
`Database schema version ${current} is newer than this Issue Flow supports (${CURRENT_SCHEMA_VERSION}). Upgrade Issue Flow before opening this database.`
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
database.exec(
|
|
498
|
+
"CREATE TABLE IF NOT EXISTS schema_migrations (version INTEGER PRIMARY KEY, name TEXT NOT NULL, applied_at TEXT NOT NULL)"
|
|
499
|
+
);
|
|
500
|
+
for (const migration of migrations) {
|
|
501
|
+
if (migration.version <= current) continue;
|
|
502
|
+
database.transaction(() => {
|
|
503
|
+
migration.up(database);
|
|
504
|
+
database.prepare("INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)").run(migration.version, migration.name, (/* @__PURE__ */ new Date()).toISOString());
|
|
505
|
+
database.exec(`PRAGMA user_version = ${migration.version}`);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
return CURRENT_SCHEMA_VERSION;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// src/storage/db/index.ts
|
|
512
|
+
var DATABASE_FILENAME = "issue-flow.db";
|
|
513
|
+
function getDatabasePath(options = {}) {
|
|
514
|
+
return join(getGlobalRoot(options), DATABASE_FILENAME);
|
|
515
|
+
}
|
|
516
|
+
var projectDatabaseOptions = /* @__PURE__ */ new Map();
|
|
517
|
+
function registerProjectDatabaseOptions(projectId, options) {
|
|
518
|
+
projectDatabaseOptions.set(projectId, options);
|
|
519
|
+
}
|
|
520
|
+
function databaseOptionsForProject(projectId) {
|
|
521
|
+
return projectDatabaseOptions.get(projectId) ?? {};
|
|
522
|
+
}
|
|
523
|
+
function resetProjectDatabaseOptions() {
|
|
524
|
+
projectDatabaseOptions.clear();
|
|
525
|
+
}
|
|
526
|
+
async function openIssueFlowDatabase(options = {}) {
|
|
527
|
+
const database = await openDatabase(getDatabasePath(options), options);
|
|
528
|
+
try {
|
|
529
|
+
migrateDatabase(database);
|
|
530
|
+
return database;
|
|
531
|
+
} catch (error) {
|
|
532
|
+
database.close();
|
|
533
|
+
throw error;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// src/storage/db/repository.ts
|
|
538
|
+
var contexts = /* @__PURE__ */ new Map();
|
|
539
|
+
var providerHealthContexts = /* @__PURE__ */ new Map();
|
|
540
|
+
var verificationContexts = /* @__PURE__ */ new Map();
|
|
541
|
+
var lastBranchContexts = /* @__PURE__ */ new Map();
|
|
542
|
+
var queueContexts = /* @__PURE__ */ new Map();
|
|
543
|
+
var agentProjectionWindows = /* @__PURE__ */ new Set();
|
|
544
|
+
function storyNumber(id) {
|
|
545
|
+
const matches = id.match(/\d+/g);
|
|
546
|
+
if (matches === null || matches.length === 0) return null;
|
|
547
|
+
const value = Number.parseInt(matches.at(-1) ?? "", 10);
|
|
548
|
+
return Number.isNaN(value) ? null : value;
|
|
549
|
+
}
|
|
550
|
+
function registerPlanRepository(context) {
|
|
551
|
+
contexts.set(context.tasksPath, context);
|
|
552
|
+
}
|
|
553
|
+
function getPlanRepository(path) {
|
|
554
|
+
return contexts.get(path);
|
|
555
|
+
}
|
|
556
|
+
function registerQueueRepository(context) {
|
|
557
|
+
queueContexts.set(context.planFile, context);
|
|
558
|
+
}
|
|
559
|
+
function getQueueRepository(path) {
|
|
560
|
+
return queueContexts.get(path);
|
|
561
|
+
}
|
|
562
|
+
function registerStorageProjections(input) {
|
|
563
|
+
if (input.providersHealthFile !== void 0) {
|
|
564
|
+
providerHealthContexts.set(input.providersHealthFile, input.context);
|
|
565
|
+
}
|
|
566
|
+
if (input.verifyFile !== void 0) verificationContexts.set(input.verifyFile, input.context);
|
|
567
|
+
if (input.lastBranchFile !== void 0)
|
|
568
|
+
lastBranchContexts.set(input.lastBranchFile, input.context);
|
|
569
|
+
}
|
|
570
|
+
function getProviderHealthRepository(path) {
|
|
571
|
+
return providerHealthContexts.get(path);
|
|
572
|
+
}
|
|
573
|
+
function getVerificationRepository(path) {
|
|
574
|
+
return verificationContexts.get(path);
|
|
575
|
+
}
|
|
576
|
+
function getLastBranchRepository(path) {
|
|
577
|
+
return lastBranchContexts.get(path);
|
|
578
|
+
}
|
|
579
|
+
async function ensurePlanRepository(path) {
|
|
580
|
+
const known = getPlanRepository(path);
|
|
581
|
+
if (known !== void 0) return known;
|
|
582
|
+
const digest = createHash("sha256").update(path).digest("hex").slice(0, 24);
|
|
583
|
+
const context = {
|
|
584
|
+
tasksPath: path,
|
|
585
|
+
projectId: `projection-${digest}`,
|
|
586
|
+
issueId: `projection-${digest}`,
|
|
587
|
+
projectRoot: dirname2(path)
|
|
588
|
+
};
|
|
589
|
+
try {
|
|
590
|
+
const plan = parsePlan(await readFile(path, "utf-8"), path);
|
|
591
|
+
await saveStoredPlan(context, plan);
|
|
592
|
+
registerPlanRepository(context);
|
|
593
|
+
return context;
|
|
594
|
+
} catch {
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
function resetPlanRepositories() {
|
|
599
|
+
contexts.clear();
|
|
600
|
+
providerHealthContexts.clear();
|
|
601
|
+
verificationContexts.clear();
|
|
602
|
+
lastBranchContexts.clear();
|
|
603
|
+
queueContexts.clear();
|
|
604
|
+
agentProjectionWindows.clear();
|
|
605
|
+
}
|
|
606
|
+
function applyStoredRetention(database, projectId, retention) {
|
|
607
|
+
const trim = (table, limit) => {
|
|
608
|
+
if (limit === void 0 || limit === 0) return;
|
|
609
|
+
const timestamp = table === "executions" ? "started_at" : table === "events" ? "occurred_at" : "updated_at";
|
|
610
|
+
database.prepare(
|
|
611
|
+
`DELETE FROM ${table} WHERE id IN (
|
|
612
|
+
SELECT id FROM ${table} WHERE project_id = ?
|
|
613
|
+
ORDER BY ${timestamp} DESC, rowid DESC LIMIT -1 OFFSET ?
|
|
614
|
+
)`
|
|
615
|
+
).run(projectId, limit);
|
|
616
|
+
};
|
|
617
|
+
trim("executions", retention?.executions);
|
|
618
|
+
trim("events", retention?.events);
|
|
619
|
+
trim("snapshots", retention?.snapshots);
|
|
620
|
+
}
|
|
621
|
+
function ensureStoredProject(database, context, timestamp) {
|
|
622
|
+
database.prepare(
|
|
623
|
+
`INSERT INTO projects (id, root, remote_url, created_at, updated_at) VALUES (?, ?, NULL, ?, ?)
|
|
624
|
+
ON CONFLICT(id) DO UPDATE SET root = excluded.root, updated_at = excluded.updated_at`
|
|
625
|
+
).run(context.projectId, context.projectRoot, timestamp, timestamp);
|
|
626
|
+
}
|
|
627
|
+
function ensureStoredIssue(database, context, timestamp) {
|
|
628
|
+
database.prepare(
|
|
629
|
+
`INSERT INTO issues (project_id, id, title, status, branch_name, created_at, updated_at)
|
|
630
|
+
VALUES (?, ?, NULL, 'pending', NULL, ?, ?)
|
|
631
|
+
ON CONFLICT(project_id, id) DO NOTHING`
|
|
632
|
+
).run(context.projectId, context.issueId, timestamp, timestamp);
|
|
633
|
+
}
|
|
634
|
+
function setAgentProjectionWindow(path, active) {
|
|
635
|
+
if (active) agentProjectionWindows.add(path);
|
|
636
|
+
else agentProjectionWindows.delete(path);
|
|
637
|
+
}
|
|
638
|
+
function parsePlan(value, path) {
|
|
639
|
+
try {
|
|
640
|
+
return taskPlanSchema.parse(JSON.parse(value));
|
|
641
|
+
} catch (error) {
|
|
642
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
643
|
+
throw new Error(`Invalid SQLite task plan for ${path}: ${detail}`, { cause: error });
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
async function withDatabase(work, options = {}) {
|
|
647
|
+
const database = await openIssueFlowDatabase(options);
|
|
648
|
+
try {
|
|
649
|
+
return work(database);
|
|
650
|
+
} finally {
|
|
651
|
+
database.close();
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
async function loadStoredPlan(context) {
|
|
655
|
+
return withDatabase((database) => {
|
|
656
|
+
const row = database.prepare("SELECT * FROM pipelines WHERE project_id = ? AND issue_id = ?").get(context.projectId, context.issueId);
|
|
657
|
+
if (row === void 0) {
|
|
658
|
+
throw new Error(`No SQLite task plan exists for issue ${context.issueId}. Run plan first.`);
|
|
659
|
+
}
|
|
660
|
+
if (row.project === null || row.project === void 0) {
|
|
661
|
+
return parsePlan(String(row.state_json), context.tasksPath);
|
|
662
|
+
}
|
|
663
|
+
const dependencies = /* @__PURE__ */ new Map();
|
|
664
|
+
for (const dependency of database.prepare(
|
|
665
|
+
"SELECT story_id, depends_on_story_id FROM story_dependencies WHERE project_id = ? AND issue_id = ?"
|
|
666
|
+
).all(context.projectId, context.issueId)) {
|
|
667
|
+
dependencies.set(dependency.story_id, [
|
|
668
|
+
...dependencies.get(dependency.story_id) ?? [],
|
|
669
|
+
dependency.depends_on_story_id
|
|
670
|
+
]);
|
|
671
|
+
}
|
|
672
|
+
const stories = database.prepare(
|
|
673
|
+
"SELECT * FROM stories WHERE project_id = ? AND issue_id = ? ORDER BY priority, rowid"
|
|
674
|
+
).all(context.projectId, context.issueId).map((story) => ({
|
|
675
|
+
id: String(story.id),
|
|
676
|
+
title: String(story.title),
|
|
677
|
+
description: String(story.description ?? ""),
|
|
678
|
+
acceptanceCriteria: JSON.parse(String(story.acceptance_criteria_json ?? "[]")),
|
|
679
|
+
priority: Number(story.priority),
|
|
680
|
+
passes: Number(story.passes) === 1,
|
|
681
|
+
notes: String(story.notes ?? ""),
|
|
682
|
+
...story.duration_seconds === null ? {} : { durationSeconds: Number(story.duration_seconds) },
|
|
683
|
+
...story.status === null ? {} : { status: String(story.status) },
|
|
684
|
+
...story.stage === null ? {} : { stage: String(story.stage) },
|
|
685
|
+
...story.stage_since === null ? {} : { stageSince: String(story.stage_since) },
|
|
686
|
+
...story.stage_detail === null ? {} : { stageDetail: String(story.stage_detail) },
|
|
687
|
+
...story.input_tokens === null ? {} : { inputTokens: Number(story.input_tokens) },
|
|
688
|
+
...story.output_tokens === null ? {} : { outputTokens: Number(story.output_tokens) },
|
|
689
|
+
...story.cache_read_tokens === null ? {} : { cacheReadTokens: Number(story.cache_read_tokens) },
|
|
690
|
+
...story.cache_creation_tokens === null ? {} : { cacheCreationTokens: Number(story.cache_creation_tokens) },
|
|
691
|
+
...dependencies.has(String(story.id)) ? { dependencies: dependencies.get(String(story.id)) } : {}
|
|
692
|
+
}));
|
|
693
|
+
const plan = {
|
|
694
|
+
project: String(row.project),
|
|
695
|
+
issueNumber: String(row.issue_number),
|
|
696
|
+
issueUrl: String(row.issue_url ?? ""),
|
|
697
|
+
branchName: String(row.branch_name ?? ""),
|
|
698
|
+
...Number(row.no_branch) === 1 ? { noBranch: true } : {},
|
|
699
|
+
...row.close_issue == null ? {} : { closeIssue: Number(row.close_issue) === 1 },
|
|
700
|
+
...row.issue_closed_at == null ? {} : { issueClosedAt: String(row.issue_closed_at) },
|
|
701
|
+
description: String(row.description ?? ""),
|
|
702
|
+
issueStatus: String(row.issue_status),
|
|
703
|
+
completedAt: row.completed_at ?? null,
|
|
704
|
+
lastAttemptAt: row.last_attempt_at ?? null,
|
|
705
|
+
lastError: row.last_error_category === null ? null : {
|
|
706
|
+
category: String(row.last_error_category),
|
|
707
|
+
message: String(row.last_error_message),
|
|
708
|
+
at: String(row.last_error_at)
|
|
709
|
+
},
|
|
710
|
+
correctionCycle: Number(row.correction_cycle),
|
|
711
|
+
maxCorrectionCycles: Number(row.max_correction_cycles),
|
|
712
|
+
lastReviewFindings: row.last_review_findings ?? null,
|
|
713
|
+
pipeline: {
|
|
714
|
+
...Number(row.analyze_completed) === 1 ? { analyzeCompleted: true } : {},
|
|
715
|
+
prdCompleted: Number(row.prd_completed) === 1,
|
|
716
|
+
jsonCompleted: Number(row.json_completed) === 1,
|
|
717
|
+
executionCompleted: Number(row.execution_completed) === 1,
|
|
718
|
+
reviewCompleted: Number(row.review_completed) === 1,
|
|
719
|
+
prCreated: Number(row.pr_created) === 1,
|
|
720
|
+
...row.pr_review_completed === null ? {} : { prReviewCompleted: Number(row.pr_review_completed) === 1 }
|
|
721
|
+
},
|
|
722
|
+
...row.run_status === null ? {} : {
|
|
723
|
+
runState: {
|
|
724
|
+
status: row.run_status,
|
|
725
|
+
currentPhase: row.run_phase ?? null,
|
|
726
|
+
attempt: Number(row.run_attempt ?? 0),
|
|
727
|
+
lastHeartbeatAt: row.run_heartbeat_at ?? null,
|
|
728
|
+
blockedReason: row.run_blocked_reason ?? null,
|
|
729
|
+
owner: row.run_owner_pid === null ? null : {
|
|
730
|
+
pid: Number(row.run_owner_pid),
|
|
731
|
+
host: String(row.run_owner_host),
|
|
732
|
+
startedAt: String(row.run_owner_started_at)
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
},
|
|
736
|
+
...row.pr_number === null ? {} : {
|
|
737
|
+
pullRequest: {
|
|
738
|
+
number: Number(row.pr_number),
|
|
739
|
+
url: String(row.pr_url),
|
|
740
|
+
headBranch: String(row.pr_head_branch),
|
|
741
|
+
createdAt: String(row.pr_created_at)
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
...row.pr_review_enabled === null ? {} : {
|
|
745
|
+
prReview: {
|
|
746
|
+
enabled: Number(row.pr_review_enabled) === 1,
|
|
747
|
+
...row.pr_review_pull_request_number === null ? {} : { pullRequestNumber: Number(row.pr_review_pull_request_number) },
|
|
748
|
+
rounds: Number(row.pr_review_rounds ?? 0),
|
|
749
|
+
...row.pr_review_recommendation === null ? {} : {
|
|
750
|
+
lastRecommendation: String(row.pr_review_recommendation)
|
|
751
|
+
},
|
|
752
|
+
...row.pr_reviewed_at === null ? {} : { lastReviewedAt: String(row.pr_reviewed_at) }
|
|
753
|
+
}
|
|
754
|
+
},
|
|
755
|
+
userStories: stories
|
|
756
|
+
};
|
|
757
|
+
const executions = database.prepare(
|
|
758
|
+
"SELECT payload_json FROM executions WHERE project_id = ? AND issue_id = ? ORDER BY started_at, rowid"
|
|
759
|
+
).all(context.projectId, context.issueId).map((execution) => JSON.parse(execution.payload_json));
|
|
760
|
+
return executions.length > 0 ? { ...plan, executions } : plan;
|
|
761
|
+
}, context.databaseOptions);
|
|
762
|
+
}
|
|
763
|
+
function writePlanRows(database, context, plan) {
|
|
764
|
+
const timestamp = plan.lastAttemptAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
765
|
+
database.prepare(
|
|
766
|
+
`INSERT INTO projects (id, root, remote_url, created_at, updated_at) VALUES (?, ?, NULL, ?, ?)
|
|
767
|
+
ON CONFLICT(id) DO UPDATE SET root = excluded.root, updated_at = excluded.updated_at`
|
|
768
|
+
).run(context.projectId, context.projectRoot, timestamp, timestamp);
|
|
769
|
+
database.prepare(
|
|
770
|
+
`INSERT INTO issues (project_id, id, title, status, branch_name, created_at, updated_at)
|
|
771
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
772
|
+
ON CONFLICT(project_id, id) DO UPDATE SET title = excluded.title, status = excluded.status,
|
|
773
|
+
branch_name = excluded.branch_name, updated_at = excluded.updated_at`
|
|
774
|
+
).run(
|
|
775
|
+
context.projectId,
|
|
776
|
+
context.issueId,
|
|
777
|
+
plan.description || null,
|
|
778
|
+
plan.issueStatus,
|
|
779
|
+
plan.branchName || null,
|
|
780
|
+
timestamp,
|
|
781
|
+
timestamp
|
|
782
|
+
);
|
|
783
|
+
database.prepare(
|
|
784
|
+
`INSERT INTO pipelines (project_id, issue_id, state_json, updated_at) VALUES (?, ?, ?, ?)
|
|
785
|
+
ON CONFLICT(project_id, issue_id) DO UPDATE SET state_json = excluded.state_json,
|
|
786
|
+
updated_at = excluded.updated_at`
|
|
787
|
+
).run(context.projectId, context.issueId, "{}", timestamp);
|
|
788
|
+
database.prepare(
|
|
789
|
+
`UPDATE pipelines SET project = ?, issue_number = ?, issue_url = ?, branch_name = ?, no_branch = ?,
|
|
790
|
+
description = ?, issue_status = ?, completed_at = ?, last_attempt_at = ?, last_error_category = ?,
|
|
791
|
+
last_error_message = ?, last_error_at = ?, correction_cycle = ?, max_correction_cycles = ?,
|
|
792
|
+
last_review_findings = ?, analyze_completed = ?, prd_completed = ?, json_completed = ?,
|
|
793
|
+
execution_completed = ?, review_completed = ?, pr_created = ?, pr_review_completed = ?, run_status = ?,
|
|
794
|
+
run_phase = ?, run_attempt = ?, run_heartbeat_at = ?, run_blocked_reason = ?, run_owner_pid = ?,
|
|
795
|
+
run_owner_host = ?, run_owner_started_at = ?, pr_number = ?, pr_url = ?, pr_head_branch = ?,
|
|
796
|
+
pr_created_at = ?, pr_review_enabled = ?, pr_review_pull_request_number = ?, pr_review_rounds = ?,
|
|
797
|
+
pr_review_recommendation = ?, pr_reviewed_at = ?, close_issue = ?, issue_closed_at = ? WHERE project_id = ? AND issue_id = ?`
|
|
798
|
+
).run(
|
|
799
|
+
plan.project,
|
|
800
|
+
String(plan.issueNumber),
|
|
801
|
+
plan.issueUrl,
|
|
802
|
+
plan.branchName,
|
|
803
|
+
plan.noBranch === true ? 1 : 0,
|
|
804
|
+
plan.description,
|
|
805
|
+
plan.issueStatus,
|
|
806
|
+
plan.completedAt,
|
|
807
|
+
plan.lastAttemptAt,
|
|
808
|
+
plan.lastError?.category ?? null,
|
|
809
|
+
plan.lastError?.message ?? null,
|
|
810
|
+
plan.lastError?.at ?? null,
|
|
811
|
+
plan.correctionCycle,
|
|
812
|
+
plan.maxCorrectionCycles,
|
|
813
|
+
plan.lastReviewFindings,
|
|
814
|
+
plan.pipeline.analyzeCompleted === true ? 1 : 0,
|
|
815
|
+
plan.pipeline.prdCompleted ? 1 : 0,
|
|
816
|
+
plan.pipeline.jsonCompleted ? 1 : 0,
|
|
817
|
+
plan.pipeline.executionCompleted ? 1 : 0,
|
|
818
|
+
plan.pipeline.reviewCompleted ? 1 : 0,
|
|
819
|
+
plan.pipeline.prCreated ? 1 : 0,
|
|
820
|
+
plan.pipeline.prReviewCompleted === void 0 ? null : plan.pipeline.prReviewCompleted ? 1 : 0,
|
|
821
|
+
plan.runState?.status ?? null,
|
|
822
|
+
plan.runState?.currentPhase ?? null,
|
|
823
|
+
plan.runState?.attempt ?? null,
|
|
824
|
+
plan.runState?.lastHeartbeatAt ?? null,
|
|
825
|
+
plan.runState?.blockedReason ?? null,
|
|
826
|
+
plan.runState?.owner?.pid ?? null,
|
|
827
|
+
plan.runState?.owner?.host ?? null,
|
|
828
|
+
plan.runState?.owner?.startedAt ?? null,
|
|
829
|
+
plan.pullRequest?.number ?? null,
|
|
830
|
+
plan.pullRequest?.url ?? null,
|
|
831
|
+
plan.pullRequest?.headBranch ?? null,
|
|
832
|
+
plan.pullRequest?.createdAt ?? null,
|
|
833
|
+
plan.prReview === void 0 ? null : plan.prReview.enabled ? 1 : 0,
|
|
834
|
+
plan.prReview?.pullRequestNumber ?? null,
|
|
835
|
+
plan.prReview?.rounds ?? null,
|
|
836
|
+
plan.prReview?.lastRecommendation ?? null,
|
|
837
|
+
plan.prReview?.lastReviewedAt ?? null,
|
|
838
|
+
plan.closeIssue === void 0 ? null : plan.closeIssue ? 1 : 0,
|
|
839
|
+
plan.issueClosedAt ?? null,
|
|
840
|
+
context.projectId,
|
|
841
|
+
context.issueId
|
|
842
|
+
);
|
|
843
|
+
database.prepare("DELETE FROM story_dependencies WHERE project_id = ? AND issue_id = ?").run(context.projectId, context.issueId);
|
|
844
|
+
database.prepare("DELETE FROM stories WHERE project_id = ? AND issue_id = ?").run(context.projectId, context.issueId);
|
|
845
|
+
for (const story of plan.userStories) {
|
|
846
|
+
database.prepare(
|
|
847
|
+
`INSERT INTO stories (project_id, issue_id, id, title, priority, passes, notes, story_number)
|
|
848
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
849
|
+
).run(
|
|
850
|
+
context.projectId,
|
|
851
|
+
context.issueId,
|
|
852
|
+
story.id,
|
|
853
|
+
story.title,
|
|
854
|
+
story.priority,
|
|
855
|
+
story.passes ? 1 : 0,
|
|
856
|
+
story.notes,
|
|
857
|
+
storyNumber(story.id)
|
|
858
|
+
);
|
|
859
|
+
database.prepare(
|
|
860
|
+
`UPDATE stories SET description = ?, acceptance_criteria_json = ?, duration_seconds = ?, status = ?,
|
|
861
|
+
stage = ?, stage_since = ?, stage_detail = ?, input_tokens = ?, output_tokens = ?,
|
|
862
|
+
cache_read_tokens = ?, cache_creation_tokens = ? WHERE project_id = ? AND issue_id = ? AND id = ?`
|
|
863
|
+
).run(
|
|
864
|
+
story.description,
|
|
865
|
+
JSON.stringify(story.acceptanceCriteria),
|
|
866
|
+
story.durationSeconds ?? null,
|
|
867
|
+
story.status ?? null,
|
|
868
|
+
story.stage ?? null,
|
|
869
|
+
story.stageSince ?? null,
|
|
870
|
+
story.stageDetail ?? null,
|
|
871
|
+
story.inputTokens ?? null,
|
|
872
|
+
story.outputTokens ?? null,
|
|
873
|
+
story.cacheReadTokens ?? null,
|
|
874
|
+
story.cacheCreationTokens ?? null,
|
|
875
|
+
context.projectId,
|
|
876
|
+
context.issueId,
|
|
877
|
+
story.id
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
for (const story of plan.userStories) {
|
|
881
|
+
for (const dependency of story.dependencies ?? []) {
|
|
882
|
+
database.prepare(
|
|
883
|
+
`INSERT INTO story_dependencies (project_id, issue_id, story_id, depends_on_story_id)
|
|
884
|
+
VALUES (?, ?, ?, ?)`
|
|
885
|
+
).run(context.projectId, context.issueId, story.id, dependency);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
if (plan.pullRequest !== void 0) {
|
|
889
|
+
const pullRequestId = `pr:${context.projectId}:${context.issueId}:${plan.pullRequest.number}`;
|
|
890
|
+
database.prepare(
|
|
891
|
+
`INSERT INTO pull_requests (id, project_id, issue_id, number, url, status, created_at)
|
|
892
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
893
|
+
ON CONFLICT(id) DO UPDATE SET url = excluded.url, status = excluded.status`
|
|
894
|
+
).run(
|
|
895
|
+
pullRequestId,
|
|
896
|
+
context.projectId,
|
|
897
|
+
context.issueId,
|
|
898
|
+
plan.pullRequest.number,
|
|
899
|
+
plan.pullRequest.url,
|
|
900
|
+
plan.pipeline.prCreated ? "created" : "pending",
|
|
901
|
+
plan.pullRequest.createdAt
|
|
902
|
+
);
|
|
903
|
+
if (plan.prReview?.lastReviewedAt !== void 0) {
|
|
904
|
+
database.prepare(
|
|
905
|
+
`INSERT INTO reviews (id, pull_request_id, status, created_at, payload_json)
|
|
906
|
+
VALUES (?, ?, ?, ?, ?)
|
|
907
|
+
ON CONFLICT(id) DO UPDATE SET status = excluded.status, created_at = excluded.created_at,
|
|
908
|
+
payload_json = excluded.payload_json`
|
|
909
|
+
).run(
|
|
910
|
+
`review:${pullRequestId}:${plan.prReview.rounds}`,
|
|
911
|
+
pullRequestId,
|
|
912
|
+
plan.prReview.lastRecommendation ?? "unknown",
|
|
913
|
+
plan.prReview.lastReviewedAt,
|
|
914
|
+
JSON.stringify(plan.prReview)
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
async function saveStoredPlan(context, plan) {
|
|
920
|
+
await withDatabase(
|
|
921
|
+
(database) => database.transaction(() => writePlanRows(database, context, plan)),
|
|
922
|
+
context.databaseOptions
|
|
923
|
+
);
|
|
924
|
+
await materializePlan(context);
|
|
925
|
+
}
|
|
926
|
+
async function materializePlan(context, plan) {
|
|
927
|
+
const projection = plan ?? await loadStoredPlan(context);
|
|
928
|
+
const content = `${JSON.stringify(projection, null, 2)}
|
|
929
|
+
`;
|
|
930
|
+
await writeFileAtomic(context.tasksPath, content);
|
|
931
|
+
const sha256 = createHash("sha256").update(content).digest("hex");
|
|
932
|
+
await withDatabase(
|
|
933
|
+
(database) => database.prepare(
|
|
934
|
+
`INSERT INTO migrated_artifacts (source_path, sha256, migrated_at, table_counts_json)
|
|
935
|
+
VALUES (?, ?, ?, ?)
|
|
936
|
+
ON CONFLICT(source_path) DO UPDATE SET sha256 = excluded.sha256,
|
|
937
|
+
migrated_at = excluded.migrated_at, table_counts_json = excluded.table_counts_json`
|
|
938
|
+
).run(context.tasksPath, sha256, (/* @__PURE__ */ new Date()).toISOString(), "{}"),
|
|
939
|
+
context.databaseOptions
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
async function saveStoredQueue(context, plan) {
|
|
943
|
+
await withDatabase(
|
|
944
|
+
(database) => database.transaction(() => {
|
|
945
|
+
ensureStoredProject(
|
|
946
|
+
database,
|
|
947
|
+
{
|
|
948
|
+
tasksPath: "",
|
|
949
|
+
projectId: context.projectId,
|
|
950
|
+
issueId: "",
|
|
951
|
+
projectRoot: context.projectRoot
|
|
952
|
+
},
|
|
953
|
+
plan.updatedAt
|
|
954
|
+
);
|
|
955
|
+
database.prepare(
|
|
956
|
+
`INSERT INTO queues (id, project_id, status, payload_json, created_at, updated_at,
|
|
957
|
+
requested_json, branch_name, no_branch, pr_review, truncated)
|
|
958
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
959
|
+
ON CONFLICT(id) DO UPDATE SET status = excluded.status, payload_json = excluded.payload_json,
|
|
960
|
+
updated_at = excluded.updated_at, requested_json = excluded.requested_json,
|
|
961
|
+
branch_name = excluded.branch_name, no_branch = excluded.no_branch,
|
|
962
|
+
pr_review = excluded.pr_review, truncated = excluded.truncated`
|
|
963
|
+
).run(
|
|
964
|
+
plan.id,
|
|
965
|
+
context.projectId,
|
|
966
|
+
plan.status,
|
|
967
|
+
JSON.stringify(plan),
|
|
968
|
+
plan.createdAt,
|
|
969
|
+
plan.updatedAt,
|
|
970
|
+
JSON.stringify(plan.requested),
|
|
971
|
+
plan.branchName,
|
|
972
|
+
plan.noBranch ? 1 : 0,
|
|
973
|
+
plan.prReview ? 1 : 0,
|
|
974
|
+
plan.truncated ? 1 : 0
|
|
975
|
+
);
|
|
976
|
+
database.prepare("DELETE FROM queue_dependencies WHERE queue_id = ?").run(plan.id);
|
|
977
|
+
database.prepare("DELETE FROM queue_issues WHERE queue_id = ?").run(plan.id);
|
|
978
|
+
for (const entry of plan.issues) {
|
|
979
|
+
database.prepare(
|
|
980
|
+
`INSERT INTO issues (project_id, id, title, status, branch_name, created_at, updated_at)
|
|
981
|
+
VALUES (?, ?, ?, ?, NULL, ?, ?)
|
|
982
|
+
ON CONFLICT(project_id, id) DO UPDATE SET title = excluded.title, status = excluded.status,
|
|
983
|
+
updated_at = excluded.updated_at`
|
|
984
|
+
).run(
|
|
985
|
+
context.projectId,
|
|
986
|
+
entry.id,
|
|
987
|
+
entry.title,
|
|
988
|
+
entry.status,
|
|
989
|
+
plan.createdAt,
|
|
990
|
+
plan.updatedAt
|
|
991
|
+
);
|
|
992
|
+
database.prepare(
|
|
993
|
+
`INSERT INTO queue_issues (queue_id, project_id, issue_id, position, status, number, title, url,
|
|
994
|
+
source, origin, role, priority, heuristic, failed_phase, last_error_category,
|
|
995
|
+
last_error_message, last_error_at, attempts, blocked_reason, started_at, completed_at)
|
|
996
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
997
|
+
).run(
|
|
998
|
+
plan.id,
|
|
999
|
+
context.projectId,
|
|
1000
|
+
entry.id,
|
|
1001
|
+
entry.position,
|
|
1002
|
+
entry.status,
|
|
1003
|
+
entry.number,
|
|
1004
|
+
entry.title,
|
|
1005
|
+
entry.url,
|
|
1006
|
+
entry.source,
|
|
1007
|
+
entry.origin,
|
|
1008
|
+
entry.role,
|
|
1009
|
+
entry.priority,
|
|
1010
|
+
entry.heuristic ? 1 : 0,
|
|
1011
|
+
entry.failedPhase,
|
|
1012
|
+
entry.lastError?.category ?? null,
|
|
1013
|
+
entry.lastError?.message ?? null,
|
|
1014
|
+
entry.lastError?.at ?? null,
|
|
1015
|
+
entry.attempts,
|
|
1016
|
+
entry.blockedReason,
|
|
1017
|
+
entry.startedAt,
|
|
1018
|
+
entry.completedAt
|
|
1019
|
+
);
|
|
1020
|
+
for (const dependency of entry.dependsOn) {
|
|
1021
|
+
database.prepare(
|
|
1022
|
+
"INSERT INTO queue_dependencies (queue_id, issue_id, depends_on_issue_id) VALUES (?, ?, ?)"
|
|
1023
|
+
).run(plan.id, entry.id, dependency);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}),
|
|
1027
|
+
context.databaseOptions
|
|
1028
|
+
);
|
|
1029
|
+
await mkdir(dirname2(context.planFile), { recursive: true });
|
|
1030
|
+
await writeFileAtomic(context.planFile, `${JSON.stringify(plan, null, 2)}
|
|
1031
|
+
`);
|
|
1032
|
+
}
|
|
1033
|
+
async function loadStoredQueue(context) {
|
|
1034
|
+
return withDatabase((database) => {
|
|
1035
|
+
const row = database.prepare("SELECT payload_json FROM queues WHERE id = ? AND project_id = ?").get(
|
|
1036
|
+
context.planFile === "" ? "" : basename(dirname2(context.planFile)),
|
|
1037
|
+
context.projectId
|
|
1038
|
+
);
|
|
1039
|
+
return row === void 0 ? null : JSON.parse(row.payload_json);
|
|
1040
|
+
}, context.databaseOptions);
|
|
1041
|
+
}
|
|
1042
|
+
async function ingestAgentPlan(context, baseline) {
|
|
1043
|
+
const submitted = parsePlan(await readFile(context.tasksPath, "utf-8"), context.tasksPath);
|
|
1044
|
+
await withDatabase(
|
|
1045
|
+
(database) => database.transaction(() => {
|
|
1046
|
+
for (const story of submitted.userStories) {
|
|
1047
|
+
database.prepare(
|
|
1048
|
+
"UPDATE stories SET passes = ?, notes = ? WHERE project_id = ? AND issue_id = ? AND id = ?"
|
|
1049
|
+
).run(story.passes ? 1 : 0, story.notes, context.projectId, context.issueId, story.id);
|
|
1050
|
+
}
|
|
1051
|
+
if (baseline !== void 0) {
|
|
1052
|
+
if (submitted.lastReviewFindings === null) {
|
|
1053
|
+
database.prepare(
|
|
1054
|
+
"UPDATE pipelines SET last_review_findings = NULL WHERE project_id = ? AND issue_id = ? AND last_review_findings IS ?"
|
|
1055
|
+
).run(context.projectId, context.issueId, baseline.lastReviewFindings);
|
|
1056
|
+
}
|
|
1057
|
+
database.prepare(
|
|
1058
|
+
`UPDATE pipelines SET last_error_category = ?, last_error_message = ?, last_error_at = ?
|
|
1059
|
+
WHERE project_id = ? AND issue_id = ? AND last_error_category IS ? AND last_error_message IS ? AND last_error_at IS ?`
|
|
1060
|
+
).run(
|
|
1061
|
+
submitted.lastError?.category ?? null,
|
|
1062
|
+
submitted.lastError?.message ?? null,
|
|
1063
|
+
submitted.lastError?.at ?? null,
|
|
1064
|
+
context.projectId,
|
|
1065
|
+
context.issueId,
|
|
1066
|
+
baseline.lastError?.category ?? null,
|
|
1067
|
+
baseline.lastError?.message ?? null,
|
|
1068
|
+
baseline.lastError?.at ?? null
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
}),
|
|
1072
|
+
context.databaseOptions
|
|
1073
|
+
);
|
|
1074
|
+
const merged = await loadStoredPlan(context);
|
|
1075
|
+
await materializePlan(context, merged);
|
|
1076
|
+
return merged;
|
|
1077
|
+
}
|
|
1078
|
+
async function ingestGeneratedPlan(context) {
|
|
1079
|
+
const plan = parsePlan(await readFile(context.tasksPath, "utf-8"), context.tasksPath);
|
|
1080
|
+
const current = await loadStoredPlan(context).catch((error) => {
|
|
1081
|
+
if (error instanceof Error && error.message.startsWith("No SQLite task plan exists"))
|
|
1082
|
+
return null;
|
|
1083
|
+
throw error;
|
|
1084
|
+
});
|
|
1085
|
+
delete plan.closeIssue;
|
|
1086
|
+
delete plan.issueClosedAt;
|
|
1087
|
+
if (current?.closeIssue !== void 0) plan.closeIssue = current.closeIssue;
|
|
1088
|
+
if (current?.issueClosedAt !== void 0) plan.issueClosedAt = current.issueClosedAt;
|
|
1089
|
+
await saveStoredPlan(context, plan);
|
|
1090
|
+
return plan;
|
|
1091
|
+
}
|
|
1092
|
+
function executionColumns(record) {
|
|
1093
|
+
const cost = record.cost;
|
|
1094
|
+
return {
|
|
1095
|
+
status: String(record.status),
|
|
1096
|
+
startedAt: String(record.startedAt),
|
|
1097
|
+
finishedAt: record.finishedAt ?? null,
|
|
1098
|
+
durationMs: record.durationMs ?? null,
|
|
1099
|
+
costStatus: cost?.status ?? "unknown",
|
|
1100
|
+
costAmount: cost?.status === "unknown" ? null : cost?.amount ?? null
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
async function saveExecution(context, execution) {
|
|
1104
|
+
await withDatabase(
|
|
1105
|
+
(database) => database.transaction(() => {
|
|
1106
|
+
const columns = executionColumns(execution);
|
|
1107
|
+
const agent = execution.agent;
|
|
1108
|
+
const model = agent?.model;
|
|
1109
|
+
const usage = execution.usage;
|
|
1110
|
+
const runId = execution.sessionId ?? execution.id;
|
|
1111
|
+
const phaseId = `phase:${runId}:${String(execution.purpose ?? "unknown")}`;
|
|
1112
|
+
ensureStoredProject(database, context, columns.startedAt);
|
|
1113
|
+
ensureStoredIssue(database, context, columns.startedAt);
|
|
1114
|
+
database.prepare(
|
|
1115
|
+
`INSERT INTO runs (id, project_id, issue_id, status, started_at, finished_at, session_id)
|
|
1116
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1117
|
+
ON CONFLICT(id) DO UPDATE SET status = excluded.status, finished_at = excluded.finished_at`
|
|
1118
|
+
).run(
|
|
1119
|
+
runId,
|
|
1120
|
+
context.projectId,
|
|
1121
|
+
context.issueId,
|
|
1122
|
+
columns.status,
|
|
1123
|
+
columns.startedAt,
|
|
1124
|
+
columns.finishedAt,
|
|
1125
|
+
execution.sessionId ?? null
|
|
1126
|
+
);
|
|
1127
|
+
database.prepare(
|
|
1128
|
+
`INSERT INTO phases
|
|
1129
|
+
(id, run_id, name, status, started_at, finished_at, duration_ms, input_tokens, output_tokens,
|
|
1130
|
+
cache_read_tokens, cache_creation_tokens, cost_status, cost_amount)
|
|
1131
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1132
|
+
ON CONFLICT(id) DO UPDATE SET status = excluded.status, finished_at = excluded.finished_at,
|
|
1133
|
+
duration_ms = excluded.duration_ms, input_tokens = excluded.input_tokens,
|
|
1134
|
+
output_tokens = excluded.output_tokens, cache_read_tokens = excluded.cache_read_tokens,
|
|
1135
|
+
cache_creation_tokens = excluded.cache_creation_tokens, cost_status = excluded.cost_status,
|
|
1136
|
+
cost_amount = excluded.cost_amount`
|
|
1137
|
+
).run(
|
|
1138
|
+
phaseId,
|
|
1139
|
+
runId,
|
|
1140
|
+
String(execution.purpose ?? "unknown"),
|
|
1141
|
+
columns.status,
|
|
1142
|
+
columns.startedAt,
|
|
1143
|
+
columns.finishedAt,
|
|
1144
|
+
columns.durationMs,
|
|
1145
|
+
usage?.inputTokens ?? null,
|
|
1146
|
+
usage?.outputTokens ?? null,
|
|
1147
|
+
usage?.cacheReadTokens ?? null,
|
|
1148
|
+
usage?.cacheCreationTokens ?? null,
|
|
1149
|
+
columns.costStatus,
|
|
1150
|
+
columns.costAmount
|
|
1151
|
+
);
|
|
1152
|
+
database.prepare(
|
|
1153
|
+
`INSERT INTO executions
|
|
1154
|
+
(id, project_id, issue_id, run_id, phase_id, status, started_at, finished_at, duration_ms, cost_status, cost_amount, payload_json)
|
|
1155
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1156
|
+
ON CONFLICT(id) DO UPDATE SET status = excluded.status, finished_at = excluded.finished_at,
|
|
1157
|
+
duration_ms = excluded.duration_ms, cost_status = excluded.cost_status, cost_amount = excluded.cost_amount,
|
|
1158
|
+
payload_json = excluded.payload_json`
|
|
1159
|
+
).run(
|
|
1160
|
+
execution.id,
|
|
1161
|
+
context.projectId,
|
|
1162
|
+
context.issueId,
|
|
1163
|
+
runId,
|
|
1164
|
+
phaseId,
|
|
1165
|
+
columns.status,
|
|
1166
|
+
columns.startedAt,
|
|
1167
|
+
columns.finishedAt,
|
|
1168
|
+
columns.durationMs,
|
|
1169
|
+
columns.costStatus,
|
|
1170
|
+
columns.costAmount,
|
|
1171
|
+
JSON.stringify(execution)
|
|
1172
|
+
);
|
|
1173
|
+
database.prepare(
|
|
1174
|
+
`UPDATE executions SET session_id = ?, purpose = ?, attempt = ?, trigger = ?, trigger_reason = ?,
|
|
1175
|
+
input_tokens = ?, output_tokens = ?, cache_read_tokens = ?, cache_creation_tokens = ?, reasoning_tokens = ?,
|
|
1176
|
+
harness = ?, provider = ?, model_requested = ?, model_resolved = ? WHERE id = ?`
|
|
1177
|
+
).run(
|
|
1178
|
+
execution.sessionId ?? null,
|
|
1179
|
+
execution.purpose ?? null,
|
|
1180
|
+
execution.attempt ?? null,
|
|
1181
|
+
execution.trigger ?? null,
|
|
1182
|
+
execution.triggerReason ?? null,
|
|
1183
|
+
usage?.inputTokens ?? null,
|
|
1184
|
+
usage?.outputTokens ?? null,
|
|
1185
|
+
usage?.cacheReadTokens ?? null,
|
|
1186
|
+
usage?.cacheCreationTokens ?? null,
|
|
1187
|
+
usage?.reasoningTokens ?? null,
|
|
1188
|
+
agent?.harness ?? null,
|
|
1189
|
+
agent?.provider ?? null,
|
|
1190
|
+
model?.requested ?? null,
|
|
1191
|
+
model?.resolved ?? null,
|
|
1192
|
+
execution.id
|
|
1193
|
+
);
|
|
1194
|
+
applyStoredRetention(database, context.projectId, context.retention);
|
|
1195
|
+
}),
|
|
1196
|
+
context.databaseOptions
|
|
1197
|
+
);
|
|
1198
|
+
if (context.projectId.startsWith("projection-") && !agentProjectionWindows.has(context.tasksPath)) {
|
|
1199
|
+
await materializePlan(context);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
async function readStoredProvidersHealth(context) {
|
|
1203
|
+
return withDatabase((database) => {
|
|
1204
|
+
const providers = Object.fromEntries(
|
|
1205
|
+
database.prepare("SELECT provider, payload_json FROM provider_health WHERE project_id = ?").all(context.projectId).map((row) => [row.provider, JSON.parse(row.payload_json)])
|
|
1206
|
+
);
|
|
1207
|
+
return { schemaVersion: 1, providers };
|
|
1208
|
+
}, context.databaseOptions);
|
|
1209
|
+
}
|
|
1210
|
+
async function updateStoredProviderHealth(context, provider, update) {
|
|
1211
|
+
return withDatabase(
|
|
1212
|
+
(database) => database.transaction(() => {
|
|
1213
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1214
|
+
ensureStoredProject(database, context, updatedAt);
|
|
1215
|
+
const previous = database.prepare("SELECT payload_json FROM provider_health WHERE project_id = ? AND provider = ?").get(context.projectId, provider);
|
|
1216
|
+
const current = previous === void 0 ? {
|
|
1217
|
+
status: "healthy",
|
|
1218
|
+
failures: [],
|
|
1219
|
+
consecutiveFailures: 0,
|
|
1220
|
+
cooldownLevel: 0,
|
|
1221
|
+
cooldownUntil: null,
|
|
1222
|
+
lastFailureKind: null,
|
|
1223
|
+
lastFailureAt: null,
|
|
1224
|
+
lastSuccessAt: null,
|
|
1225
|
+
probeInFlight: false,
|
|
1226
|
+
probeStartedAt: null
|
|
1227
|
+
} : JSON.parse(previous.payload_json);
|
|
1228
|
+
const next = update(current);
|
|
1229
|
+
database.prepare(
|
|
1230
|
+
`INSERT INTO provider_health
|
|
1231
|
+
(project_id, provider, payload_json, updated_at, status, consecutive_failures, cooldown_level,
|
|
1232
|
+
cooldown_until, last_failure_kind, last_failure_at, last_success_at, probe_in_flight, probe_started_at)
|
|
1233
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1234
|
+
ON CONFLICT(project_id, provider) DO UPDATE SET payload_json = excluded.payload_json,
|
|
1235
|
+
updated_at = excluded.updated_at, status = excluded.status,
|
|
1236
|
+
consecutive_failures = excluded.consecutive_failures, cooldown_level = excluded.cooldown_level,
|
|
1237
|
+
cooldown_until = excluded.cooldown_until, last_failure_kind = excluded.last_failure_kind,
|
|
1238
|
+
last_failure_at = excluded.last_failure_at, last_success_at = excluded.last_success_at,
|
|
1239
|
+
probe_in_flight = excluded.probe_in_flight, probe_started_at = excluded.probe_started_at`
|
|
1240
|
+
).run(
|
|
1241
|
+
context.projectId,
|
|
1242
|
+
provider,
|
|
1243
|
+
JSON.stringify(next),
|
|
1244
|
+
updatedAt,
|
|
1245
|
+
next.status,
|
|
1246
|
+
next.consecutiveFailures,
|
|
1247
|
+
next.cooldownLevel,
|
|
1248
|
+
next.cooldownUntil,
|
|
1249
|
+
next.lastFailureKind,
|
|
1250
|
+
next.lastFailureAt,
|
|
1251
|
+
next.lastSuccessAt,
|
|
1252
|
+
next.probeInFlight ? 1 : 0,
|
|
1253
|
+
next.probeStartedAt
|
|
1254
|
+
);
|
|
1255
|
+
database.prepare("DELETE FROM provider_health_failures WHERE project_id = ? AND provider = ?").run(context.projectId, provider);
|
|
1256
|
+
for (const failure of next.failures) {
|
|
1257
|
+
database.prepare(
|
|
1258
|
+
`INSERT INTO provider_health_failures (project_id, provider, occurred_at, kind)
|
|
1259
|
+
VALUES (?, ?, ?, ?)`
|
|
1260
|
+
).run(context.projectId, provider, failure.at, failure.kind);
|
|
1261
|
+
}
|
|
1262
|
+
return next;
|
|
1263
|
+
}),
|
|
1264
|
+
context.databaseOptions
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
async function saveStoredVerification(context, evidence) {
|
|
1268
|
+
await withDatabase((database) => {
|
|
1269
|
+
database.transaction(() => {
|
|
1270
|
+
const createdAt = String(evidence.at ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
1271
|
+
ensureStoredProject(database, context, createdAt);
|
|
1272
|
+
ensureStoredIssue(database, context, createdAt);
|
|
1273
|
+
database.prepare(
|
|
1274
|
+
`INSERT INTO verifications (id, project_id, issue_id, status, created_at, payload_json)
|
|
1275
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
1276
|
+
).run(
|
|
1277
|
+
randomUUID(),
|
|
1278
|
+
context.projectId,
|
|
1279
|
+
context.issueId,
|
|
1280
|
+
String(evidence.verdict ?? "unknown"),
|
|
1281
|
+
createdAt,
|
|
1282
|
+
JSON.stringify(evidence)
|
|
1283
|
+
);
|
|
1284
|
+
});
|
|
1285
|
+
}, context.databaseOptions);
|
|
1286
|
+
}
|
|
1287
|
+
async function loadStoredLastBranch(context) {
|
|
1288
|
+
return withDatabase((database) => {
|
|
1289
|
+
const row = database.prepare(
|
|
1290
|
+
`SELECT payload_json FROM audit_log WHERE project_id = ? AND action = ?
|
|
1291
|
+
ORDER BY occurred_at DESC, rowid DESC LIMIT 1`
|
|
1292
|
+
).get(context.projectId, `last_branch:${context.issueId}`);
|
|
1293
|
+
if (row === void 0) return null;
|
|
1294
|
+
const payload = JSON.parse(row.payload_json);
|
|
1295
|
+
return typeof payload.branch === "string" && payload.branch !== "" ? payload.branch : null;
|
|
1296
|
+
}, context.databaseOptions);
|
|
1297
|
+
}
|
|
1298
|
+
async function saveStoredLastBranch(context, branch) {
|
|
1299
|
+
await withDatabase((database) => {
|
|
1300
|
+
database.transaction(() => {
|
|
1301
|
+
const occurredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1302
|
+
ensureStoredProject(database, context, occurredAt);
|
|
1303
|
+
database.prepare(
|
|
1304
|
+
`INSERT INTO audit_log (id, project_id, occurred_at, action, payload_json)
|
|
1305
|
+
VALUES (?, ?, ?, ?, ?)`
|
|
1306
|
+
).run(
|
|
1307
|
+
randomUUID(),
|
|
1308
|
+
context.projectId,
|
|
1309
|
+
occurredAt,
|
|
1310
|
+
`last_branch:${context.issueId}`,
|
|
1311
|
+
JSON.stringify({ branch })
|
|
1312
|
+
);
|
|
1313
|
+
});
|
|
1314
|
+
}, context.databaseOptions);
|
|
1315
|
+
}
|
|
1316
|
+
async function saveStoredUserStoryNumbering(context, decision) {
|
|
1317
|
+
await withDatabase((database) => {
|
|
1318
|
+
database.transaction(() => {
|
|
1319
|
+
ensureStoredProject(database, { ...context, tasksPath: "", issueId: "" }, decision.decidedAt);
|
|
1320
|
+
database.prepare(
|
|
1321
|
+
`INSERT INTO user_story_numbering (project_id, next_number, source, issue_id, decided_at, detail)
|
|
1322
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
1323
|
+
ON CONFLICT(project_id) DO UPDATE SET next_number = excluded.next_number,
|
|
1324
|
+
source = excluded.source, issue_id = excluded.issue_id, decided_at = excluded.decided_at,
|
|
1325
|
+
detail = excluded.detail`
|
|
1326
|
+
).run(
|
|
1327
|
+
context.projectId,
|
|
1328
|
+
decision.nextNumber,
|
|
1329
|
+
decision.source,
|
|
1330
|
+
decision.issueNumber,
|
|
1331
|
+
decision.decidedAt,
|
|
1332
|
+
decision.detail ?? null
|
|
1333
|
+
);
|
|
1334
|
+
});
|
|
1335
|
+
}, context.databaseOptions);
|
|
1336
|
+
}
|
|
1337
|
+
async function saveSessionEvent(context, input) {
|
|
1338
|
+
await withDatabase(
|
|
1339
|
+
(database) => database.transaction(() => {
|
|
1340
|
+
const updatedAt = input.snapshot.updatedAt ?? input.event.at;
|
|
1341
|
+
const startedAt = input.snapshot.startedAt ?? input.event.at;
|
|
1342
|
+
database.prepare(
|
|
1343
|
+
`INSERT INTO projects (id, root, remote_url, created_at, updated_at) VALUES (?, ?, NULL, ?, ?)
|
|
1344
|
+
ON CONFLICT(id) DO UPDATE SET root = excluded.root, updated_at = excluded.updated_at`
|
|
1345
|
+
).run(context.projectId, context.projectRoot, startedAt, updatedAt);
|
|
1346
|
+
database.prepare(
|
|
1347
|
+
`INSERT INTO issues (project_id, id, title, status, branch_name, created_at, updated_at)
|
|
1348
|
+
VALUES (?, ?, NULL, ?, NULL, ?, ?)
|
|
1349
|
+
ON CONFLICT(project_id, id) DO UPDATE SET updated_at = excluded.updated_at`
|
|
1350
|
+
).run(context.projectId, context.issueId, "in_progress", startedAt, updatedAt);
|
|
1351
|
+
database.prepare(
|
|
1352
|
+
`INSERT INTO runs (id, project_id, issue_id, status, started_at, finished_at, session_id, heartbeat_at, pid, host)
|
|
1353
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1354
|
+
ON CONFLICT(id) DO UPDATE SET status = excluded.status, finished_at = excluded.finished_at,
|
|
1355
|
+
heartbeat_at = excluded.heartbeat_at, pid = excluded.pid, host = excluded.host`
|
|
1356
|
+
).run(
|
|
1357
|
+
input.sessionId,
|
|
1358
|
+
context.projectId,
|
|
1359
|
+
context.issueId,
|
|
1360
|
+
input.snapshot.status,
|
|
1361
|
+
startedAt,
|
|
1362
|
+
input.snapshot.endedAt,
|
|
1363
|
+
input.sessionId,
|
|
1364
|
+
updatedAt,
|
|
1365
|
+
process.pid,
|
|
1366
|
+
process.env.HOSTNAME ?? null
|
|
1367
|
+
);
|
|
1368
|
+
database.prepare(
|
|
1369
|
+
`INSERT INTO events (id, project_id, run_id, occurred_at, kind, payload_json, session_id, sequence)
|
|
1370
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
1371
|
+
).run(
|
|
1372
|
+
randomUUID(),
|
|
1373
|
+
context.projectId,
|
|
1374
|
+
input.sessionId,
|
|
1375
|
+
input.event.at,
|
|
1376
|
+
input.event.type,
|
|
1377
|
+
JSON.stringify(input.event),
|
|
1378
|
+
input.sessionId,
|
|
1379
|
+
input.sequence
|
|
1380
|
+
);
|
|
1381
|
+
database.prepare(
|
|
1382
|
+
`INSERT INTO snapshots (id, project_id, run_id, created_at, payload_json, issue_id, session_id, updated_at)
|
|
1383
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
1384
|
+
).run(
|
|
1385
|
+
randomUUID(),
|
|
1386
|
+
context.projectId,
|
|
1387
|
+
input.sessionId,
|
|
1388
|
+
updatedAt,
|
|
1389
|
+
JSON.stringify(input.snapshot),
|
|
1390
|
+
context.issueId,
|
|
1391
|
+
input.sessionId,
|
|
1392
|
+
updatedAt
|
|
1393
|
+
);
|
|
1394
|
+
applyStoredRetention(database, context.projectId, context.retention);
|
|
1395
|
+
}),
|
|
1396
|
+
context.databaseOptions
|
|
1397
|
+
);
|
|
1398
|
+
}
|
|
1399
|
+
async function listStoredSessions(input) {
|
|
1400
|
+
return withDatabase((database) => {
|
|
1401
|
+
return database.prepare(
|
|
1402
|
+
`SELECT project_id, issue_id, session_id, payload_json, updated_at FROM snapshots AS current
|
|
1403
|
+
WHERE current.session_id IS NOT NULL AND current.updated_at >= ?
|
|
1404
|
+
AND current.id = (
|
|
1405
|
+
SELECT newer.id FROM snapshots AS newer
|
|
1406
|
+
WHERE newer.project_id = current.project_id AND newer.session_id = current.session_id
|
|
1407
|
+
ORDER BY newer.updated_at DESC, newer.rowid DESC LIMIT 1
|
|
1408
|
+
)
|
|
1409
|
+
ORDER BY current.updated_at DESC, current.rowid DESC`
|
|
1410
|
+
).all(input.activeSince).map((row) => ({
|
|
1411
|
+
projectId: row.project_id,
|
|
1412
|
+
issueId: row.issue_id,
|
|
1413
|
+
sessionId: row.session_id,
|
|
1414
|
+
snapshot: JSON.parse(row.payload_json),
|
|
1415
|
+
updatedAt: row.updated_at
|
|
1416
|
+
}));
|
|
1417
|
+
}, input.databaseOptions);
|
|
1418
|
+
}
|
|
1419
|
+
async function listStoredRunSnapshots(input = {}) {
|
|
1420
|
+
return withDatabase((database) => {
|
|
1421
|
+
return database.prepare(
|
|
1422
|
+
`SELECT run.project_id, run.issue_id, run.session_id, snapshot.payload_json, snapshot.updated_at
|
|
1423
|
+
FROM runs AS run
|
|
1424
|
+
JOIN snapshots AS snapshot ON snapshot.id = (
|
|
1425
|
+
SELECT latest.id FROM snapshots AS latest
|
|
1426
|
+
WHERE latest.run_id = run.id ORDER BY latest.updated_at DESC, latest.rowid DESC LIMIT 1
|
|
1427
|
+
)
|
|
1428
|
+
WHERE run.session_id IS NOT NULL`
|
|
1429
|
+
).all().map((row) => ({
|
|
1430
|
+
projectId: row.project_id,
|
|
1431
|
+
issueId: row.issue_id,
|
|
1432
|
+
sessionId: row.session_id,
|
|
1433
|
+
snapshot: JSON.parse(row.payload_json),
|
|
1434
|
+
updatedAt: row.updated_at
|
|
1435
|
+
}));
|
|
1436
|
+
}, input.databaseOptions);
|
|
1437
|
+
}
|
|
1438
|
+
async function listStoredSessionEvents(input) {
|
|
1439
|
+
return withDatabase(
|
|
1440
|
+
(database) => database.prepare(
|
|
1441
|
+
`SELECT sequence, payload_json FROM events
|
|
1442
|
+
WHERE project_id = ? AND session_id = ? ORDER BY sequence, rowid`
|
|
1443
|
+
).all(input.projectId, input.sessionId).map((row) => ({ seq: row.sequence, event: JSON.parse(row.payload_json) })),
|
|
1444
|
+
input.databaseOptions
|
|
1445
|
+
);
|
|
1446
|
+
}
|
|
1447
|
+
async function touchStoredSession(context, sessionId, updatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1448
|
+
await withDatabase((database) => {
|
|
1449
|
+
database.transaction(() => {
|
|
1450
|
+
database.prepare("UPDATE runs SET heartbeat_at = ? WHERE id = ? AND project_id = ?").run(updatedAt, sessionId, context.projectId);
|
|
1451
|
+
const latest = database.prepare(
|
|
1452
|
+
`SELECT id FROM snapshots WHERE project_id = ? AND session_id = ?
|
|
1453
|
+
ORDER BY updated_at DESC, rowid DESC LIMIT 1`
|
|
1454
|
+
).get(context.projectId, sessionId);
|
|
1455
|
+
if (latest !== void 0) {
|
|
1456
|
+
database.prepare("UPDATE snapshots SET updated_at = ? WHERE id = ?").run(updatedAt, latest.id);
|
|
1457
|
+
}
|
|
1458
|
+
});
|
|
1459
|
+
}, context.databaseOptions);
|
|
1460
|
+
}
|
|
1461
|
+
async function loadExecution(context, id) {
|
|
1462
|
+
return withDatabase((database) => {
|
|
1463
|
+
const row = database.prepare(
|
|
1464
|
+
"SELECT payload_json FROM executions WHERE id = ? AND project_id = ? AND issue_id = ?"
|
|
1465
|
+
).get(id, context.projectId, context.issueId);
|
|
1466
|
+
return row === void 0 ? null : JSON.parse(row.payload_json);
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
async function listStoredExecutions(input) {
|
|
1470
|
+
return withDatabase((database) => {
|
|
1471
|
+
const clauses = ["project_id = ?"];
|
|
1472
|
+
const values = [input.projectId];
|
|
1473
|
+
if (input.issueId !== void 0) {
|
|
1474
|
+
clauses.push("issue_id = ?");
|
|
1475
|
+
values.push(input.issueId);
|
|
1476
|
+
}
|
|
1477
|
+
if (input.since !== void 0) {
|
|
1478
|
+
clauses.push("started_at >= ?");
|
|
1479
|
+
values.push(input.since);
|
|
1480
|
+
}
|
|
1481
|
+
return database.prepare(
|
|
1482
|
+
`SELECT payload_json FROM executions WHERE ${clauses.join(" AND ")} ORDER BY started_at, rowid`
|
|
1483
|
+
).all(...values).map((row) => JSON.parse(row.payload_json));
|
|
1484
|
+
}, input.databaseOptions ?? databaseOptionsForProject(input.projectId));
|
|
1485
|
+
}
|
|
1486
|
+
async function findHighestStoredUserStoryNumber(input) {
|
|
1487
|
+
return withDatabase((database) => {
|
|
1488
|
+
const statement = input.excludeIssueId === void 0 ? "SELECT issue_id, id, story_number FROM stories WHERE project_id = ? AND story_number IS NOT NULL ORDER BY story_number DESC LIMIT 1" : "SELECT issue_id, id, story_number FROM stories WHERE project_id = ? AND issue_id <> ? AND story_number IS NOT NULL ORDER BY story_number DESC LIMIT 1";
|
|
1489
|
+
const row = database.prepare(statement).get(
|
|
1490
|
+
input.projectId,
|
|
1491
|
+
...input.excludeIssueId === void 0 ? [] : [input.excludeIssueId]
|
|
1492
|
+
);
|
|
1493
|
+
return row === void 0 ? null : { number: row.story_number, issueId: row.issue_id, storyId: row.id };
|
|
1494
|
+
}, input.databaseOptions ?? databaseOptionsForProject(input.projectId));
|
|
1495
|
+
}
|
|
1496
|
+
async function exportStoredState(options = {}) {
|
|
1497
|
+
return withDatabase((database) => {
|
|
1498
|
+
const tables = [
|
|
1499
|
+
"projects",
|
|
1500
|
+
"issues",
|
|
1501
|
+
"pipelines",
|
|
1502
|
+
"stories",
|
|
1503
|
+
"story_dependencies",
|
|
1504
|
+
"runs",
|
|
1505
|
+
"phases",
|
|
1506
|
+
"executions",
|
|
1507
|
+
"events",
|
|
1508
|
+
"snapshots",
|
|
1509
|
+
"pull_requests",
|
|
1510
|
+
"reviews",
|
|
1511
|
+
"verifications",
|
|
1512
|
+
"provider_health",
|
|
1513
|
+
"queues",
|
|
1514
|
+
"queue_issues",
|
|
1515
|
+
"queue_dependencies",
|
|
1516
|
+
"user_story_numbering",
|
|
1517
|
+
"provider_health_failures",
|
|
1518
|
+
"migrated_artifacts",
|
|
1519
|
+
"audit_log"
|
|
1520
|
+
];
|
|
1521
|
+
return Object.fromEntries(
|
|
1522
|
+
tables.map((table) => [table, database.prepare(`SELECT * FROM ${table}`).all()])
|
|
1523
|
+
);
|
|
1524
|
+
}, options);
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
export {
|
|
1528
|
+
openDatabase,
|
|
1529
|
+
CURRENT_SCHEMA_VERSION,
|
|
1530
|
+
getDatabasePath,
|
|
1531
|
+
registerProjectDatabaseOptions,
|
|
1532
|
+
databaseOptionsForProject,
|
|
1533
|
+
resetProjectDatabaseOptions,
|
|
1534
|
+
openIssueFlowDatabase,
|
|
1535
|
+
registerPlanRepository,
|
|
1536
|
+
getPlanRepository,
|
|
1537
|
+
registerQueueRepository,
|
|
1538
|
+
getQueueRepository,
|
|
1539
|
+
registerStorageProjections,
|
|
1540
|
+
getProviderHealthRepository,
|
|
1541
|
+
getVerificationRepository,
|
|
1542
|
+
getLastBranchRepository,
|
|
1543
|
+
ensurePlanRepository,
|
|
1544
|
+
resetPlanRepositories,
|
|
1545
|
+
setAgentProjectionWindow,
|
|
1546
|
+
loadStoredPlan,
|
|
1547
|
+
writePlanRows,
|
|
1548
|
+
saveStoredPlan,
|
|
1549
|
+
materializePlan,
|
|
1550
|
+
saveStoredQueue,
|
|
1551
|
+
loadStoredQueue,
|
|
1552
|
+
ingestAgentPlan,
|
|
1553
|
+
ingestGeneratedPlan,
|
|
1554
|
+
saveExecution,
|
|
1555
|
+
readStoredProvidersHealth,
|
|
1556
|
+
updateStoredProviderHealth,
|
|
1557
|
+
saveStoredVerification,
|
|
1558
|
+
loadStoredLastBranch,
|
|
1559
|
+
saveStoredLastBranch,
|
|
1560
|
+
saveStoredUserStoryNumbering,
|
|
1561
|
+
saveSessionEvent,
|
|
1562
|
+
listStoredSessions,
|
|
1563
|
+
listStoredRunSnapshots,
|
|
1564
|
+
listStoredSessionEvents,
|
|
1565
|
+
touchStoredSession,
|
|
1566
|
+
loadExecution,
|
|
1567
|
+
listStoredExecutions,
|
|
1568
|
+
findHighestStoredUserStoryNumber,
|
|
1569
|
+
exportStoredState
|
|
1570
|
+
};
|
|
1571
|
+
//# sourceMappingURL=chunk-7MJGYPHF.js.map
|