@askdkc/kiokuko 0.1.17 → 0.1.19

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 (534) hide show
  1. package/README.ja.md +91 -112
  2. package/README.ko.md +90 -102
  3. package/README.md +103 -262
  4. package/README.zh-CN.md +88 -102
  5. package/dist/agent-file/atomic-write.d.ts +90 -3
  6. package/dist/agent-file/atomic-write.d.ts.map +1 -1
  7. package/dist/agent-file/atomic-write.js +990 -14
  8. package/dist/agent-file/atomic-write.js.map +1 -1
  9. package/dist/agent-file/managed-block.d.ts +12 -1
  10. package/dist/agent-file/managed-block.d.ts.map +1 -1
  11. package/dist/agent-file/managed-block.js +49 -4
  12. package/dist/agent-file/managed-block.js.map +1 -1
  13. package/dist/agent-file/render.d.ts +1 -1
  14. package/dist/agent-file/render.d.ts.map +1 -1
  15. package/dist/agent-file/render.js +10 -7
  16. package/dist/agent-file/render.js.map +1 -1
  17. package/dist/akinator/agent-task.d.ts +11 -22
  18. package/dist/akinator/agent-task.d.ts.map +1 -1
  19. package/dist/akinator/agent-task.js +454 -99
  20. package/dist/akinator/agent-task.js.map +1 -1
  21. package/dist/akinator/capabilities.d.ts +18 -6
  22. package/dist/akinator/capabilities.d.ts.map +1 -1
  23. package/dist/akinator/capabilities.js +72 -35
  24. package/dist/akinator/capabilities.js.map +1 -1
  25. package/dist/akinator/capability-binding.d.ts +11 -0
  26. package/dist/akinator/capability-binding.d.ts.map +1 -0
  27. package/dist/akinator/capability-binding.js +56 -0
  28. package/dist/akinator/capability-binding.js.map +1 -0
  29. package/dist/akinator/domain.js +2 -2
  30. package/dist/akinator/domain.js.map +1 -1
  31. package/dist/akinator/knowledge-path.d.ts.map +1 -1
  32. package/dist/akinator/knowledge-path.js +409 -39
  33. package/dist/akinator/knowledge-path.js.map +1 -1
  34. package/dist/akinator/orchestrator.d.ts +3 -4
  35. package/dist/akinator/orchestrator.d.ts.map +1 -1
  36. package/dist/akinator/orchestrator.js +1 -4
  37. package/dist/akinator/orchestrator.js.map +1 -1
  38. package/dist/akinator/service.d.ts +0 -4
  39. package/dist/akinator/service.d.ts.map +1 -1
  40. package/dist/akinator/service.js +19 -35
  41. package/dist/akinator/service.js.map +1 -1
  42. package/dist/akinator/skill-discovery-attempt.d.ts +20 -0
  43. package/dist/akinator/skill-discovery-attempt.d.ts.map +1 -0
  44. package/dist/akinator/skill-discovery-attempt.js +390 -0
  45. package/dist/akinator/skill-discovery-attempt.js.map +1 -0
  46. package/dist/akinator/store.d.ts.map +1 -1
  47. package/dist/akinator/store.js +107 -52
  48. package/dist/akinator/store.js.map +1 -1
  49. package/dist/akinator/types.d.ts +0 -12
  50. package/dist/akinator/types.d.ts.map +1 -1
  51. package/dist/bin/kiokuko.js +0 -0
  52. package/dist/cli.d.ts +2 -0
  53. package/dist/cli.d.ts.map +1 -1
  54. package/dist/cli.js +534 -148
  55. package/dist/cli.js.map +1 -1
  56. package/dist/client/server-client.d.ts +5 -3
  57. package/dist/client/server-client.d.ts.map +1 -1
  58. package/dist/client/server-client.js +486 -181
  59. package/dist/client/server-client.js.map +1 -1
  60. package/dist/commands/agent.d.ts +2 -0
  61. package/dist/commands/agent.d.ts.map +1 -1
  62. package/dist/commands/agent.js +861 -86
  63. package/dist/commands/agent.js.map +1 -1
  64. package/dist/commands/backup.d.ts +2 -1
  65. package/dist/commands/backup.d.ts.map +1 -1
  66. package/dist/commands/backup.js +64 -8
  67. package/dist/commands/backup.js.map +1 -1
  68. package/dist/commands/doctor.d.ts +5 -1
  69. package/dist/commands/doctor.d.ts.map +1 -1
  70. package/dist/commands/doctor.js +70 -26
  71. package/dist/commands/doctor.js.map +1 -1
  72. package/dist/commands/export.d.ts +0 -4
  73. package/dist/commands/export.d.ts.map +1 -1
  74. package/dist/commands/export.js +191 -32
  75. package/dist/commands/export.js.map +1 -1
  76. package/dist/commands/import.d.ts +10 -1
  77. package/dist/commands/import.d.ts.map +1 -1
  78. package/dist/commands/import.js +592 -113
  79. package/dist/commands/import.js.map +1 -1
  80. package/dist/commands/init.d.ts +19 -1
  81. package/dist/commands/init.d.ts.map +1 -1
  82. package/dist/commands/init.js +569 -22
  83. package/dist/commands/init.js.map +1 -1
  84. package/dist/commands/purge.d.ts.map +1 -1
  85. package/dist/commands/purge.js +3 -0
  86. package/dist/commands/purge.js.map +1 -1
  87. package/dist/commands/setup.d.ts +34 -8
  88. package/dist/commands/setup.d.ts.map +1 -1
  89. package/dist/commands/setup.js +418 -86
  90. package/dist/commands/setup.js.map +1 -1
  91. package/dist/commands/skills.d.ts +9 -0
  92. package/dist/commands/skills.d.ts.map +1 -0
  93. package/dist/commands/skills.js +317 -0
  94. package/dist/commands/skills.js.map +1 -0
  95. package/dist/commands/use.d.ts +10 -2
  96. package/dist/commands/use.d.ts.map +1 -1
  97. package/dist/commands/use.js +851 -73
  98. package/dist/commands/use.js.map +1 -1
  99. package/dist/config/paths.d.ts +11 -1
  100. package/dist/config/paths.d.ts.map +1 -1
  101. package/dist/config/paths.js +64 -29
  102. package/dist/config/paths.js.map +1 -1
  103. package/dist/config/project-config.d.ts +1 -0
  104. package/dist/config/project-config.d.ts.map +1 -1
  105. package/dist/config/project-config.js +70 -32
  106. package/dist/config/project-config.js.map +1 -1
  107. package/dist/context/broker.d.ts +28 -14
  108. package/dist/context/broker.d.ts.map +1 -1
  109. package/dist/context/broker.js +604 -145
  110. package/dist/context/broker.js.map +1 -1
  111. package/dist/context/delivery.d.ts +6 -21
  112. package/dist/context/delivery.d.ts.map +1 -1
  113. package/dist/context/delivery.js +196 -147
  114. package/dist/context/delivery.js.map +1 -1
  115. package/dist/context/feedback.d.ts +11 -12
  116. package/dist/context/feedback.d.ts.map +1 -1
  117. package/dist/context/feedback.js +229 -261
  118. package/dist/context/feedback.js.map +1 -1
  119. package/dist/context/ranking.d.ts +1 -12
  120. package/dist/context/ranking.d.ts.map +1 -1
  121. package/dist/context/ranking.js +13 -4
  122. package/dist/context/ranking.js.map +1 -1
  123. package/dist/context/recommendations.d.ts +0 -5
  124. package/dist/context/recommendations.d.ts.map +1 -1
  125. package/dist/context/recommendations.js +0 -1
  126. package/dist/context/recommendations.js.map +1 -1
  127. package/dist/context/run-state.d.ts +26 -0
  128. package/dist/context/run-state.d.ts.map +1 -0
  129. package/dist/context/run-state.js +399 -0
  130. package/dist/context/run-state.js.map +1 -0
  131. package/dist/context/scoped-broker.d.ts +17 -5
  132. package/dist/context/scoped-broker.d.ts.map +1 -1
  133. package/dist/context/scoped-broker.js +512 -106
  134. package/dist/context/scoped-broker.js.map +1 -1
  135. package/dist/context/selection-state.d.ts +19 -0
  136. package/dist/context/selection-state.d.ts.map +1 -0
  137. package/dist/context/selection-state.js +250 -0
  138. package/dist/context/selection-state.js.map +1 -0
  139. package/dist/db/adapter.d.ts +5 -3
  140. package/dist/db/adapter.d.ts.map +1 -1
  141. package/dist/db/adapter.js +6 -3
  142. package/dist/db/adapter.js.map +1 -1
  143. package/dist/db/capabilities.d.ts.map +1 -1
  144. package/dist/db/capabilities.js +34 -16
  145. package/dist/db/capabilities.js.map +1 -1
  146. package/dist/db/connection.d.ts +8 -0
  147. package/dist/db/connection.d.ts.map +1 -1
  148. package/dist/db/connection.js +55 -26
  149. package/dist/db/connection.js.map +1 -1
  150. package/dist/db/migrate.d.ts +28 -7
  151. package/dist/db/migrate.d.ts.map +1 -1
  152. package/dist/db/migrate.js +106 -78
  153. package/dist/db/migrate.js.map +1 -1
  154. package/dist/db/sqlite-retry.d.ts +11 -0
  155. package/dist/db/sqlite-retry.d.ts.map +1 -0
  156. package/dist/db/sqlite-retry.js +66 -0
  157. package/dist/db/sqlite-retry.js.map +1 -0
  158. package/dist/db/transaction.d.ts +13 -0
  159. package/dist/db/transaction.d.ts.map +1 -1
  160. package/dist/db/transaction.js +53 -24
  161. package/dist/db/transaction.js.map +1 -1
  162. package/dist/db/upgrade-backup.d.ts +58 -2
  163. package/dist/db/upgrade-backup.d.ts.map +1 -1
  164. package/dist/db/upgrade-backup.js +757 -28
  165. package/dist/db/upgrade-backup.js.map +1 -1
  166. package/dist/errors.js +1 -1
  167. package/dist/errors.js.map +1 -1
  168. package/dist/gateway/agent-service.d.ts +4 -1
  169. package/dist/gateway/agent-service.d.ts.map +1 -1
  170. package/dist/gateway/agent-service.js +71 -58
  171. package/dist/gateway/agent-service.js.map +1 -1
  172. package/dist/gateway/checkpoint-service.d.ts +1 -0
  173. package/dist/gateway/checkpoint-service.d.ts.map +1 -1
  174. package/dist/gateway/checkpoint-service.js +47 -24
  175. package/dist/gateway/checkpoint-service.js.map +1 -1
  176. package/dist/ledger/archive.d.ts +1 -4
  177. package/dist/ledger/archive.d.ts.map +1 -1
  178. package/dist/ledger/archive.js +77 -39
  179. package/dist/ledger/archive.js.map +1 -1
  180. package/dist/ledger/maintenance.d.ts +0 -11
  181. package/dist/ledger/maintenance.d.ts.map +1 -1
  182. package/dist/ledger/maintenance.js +24 -40
  183. package/dist/ledger/maintenance.js.map +1 -1
  184. package/dist/ledger/projection.d.ts +0 -1
  185. package/dist/ledger/projection.d.ts.map +1 -1
  186. package/dist/ledger/projection.js.map +1 -1
  187. package/dist/ledger/promotion.d.ts +0 -9
  188. package/dist/ledger/promotion.d.ts.map +1 -1
  189. package/dist/ledger/promotion.js.map +1 -1
  190. package/dist/ledger/query.d.ts +1 -15
  191. package/dist/ledger/query.d.ts.map +1 -1
  192. package/dist/ledger/query.js.map +1 -1
  193. package/dist/ledger/store.d.ts +0 -1
  194. package/dist/ledger/store.d.ts.map +1 -1
  195. package/dist/ledger/store.js +11 -8
  196. package/dist/ledger/store.js.map +1 -1
  197. package/dist/ledger/types.d.ts +0 -4
  198. package/dist/ledger/types.d.ts.map +1 -1
  199. package/dist/ledger/types.js.map +1 -1
  200. package/dist/ledger/validate.d.ts.map +1 -1
  201. package/dist/ledger/validate.js +0 -5
  202. package/dist/ledger/validate.js.map +1 -1
  203. package/dist/mcp/server.d.ts +4 -0
  204. package/dist/mcp/server.d.ts.map +1 -1
  205. package/dist/mcp/server.js +105 -54
  206. package/dist/mcp/server.js.map +1 -1
  207. package/dist/memory/audit.d.ts +0 -1
  208. package/dist/memory/audit.d.ts.map +1 -1
  209. package/dist/memory/audit.js +0 -1
  210. package/dist/memory/audit.js.map +1 -1
  211. package/dist/memory/curator.d.ts.map +1 -1
  212. package/dist/memory/curator.js +172 -96
  213. package/dist/memory/curator.js.map +1 -1
  214. package/dist/memory/entries.d.ts +12 -2
  215. package/dist/memory/entries.d.ts.map +1 -1
  216. package/dist/memory/entries.js +198 -114
  217. package/dist/memory/entries.js.map +1 -1
  218. package/dist/memory/federated-retrieval.d.ts +12 -1
  219. package/dist/memory/federated-retrieval.d.ts.map +1 -1
  220. package/dist/memory/federated-retrieval.js +183 -70
  221. package/dist/memory/federated-retrieval.js.map +1 -1
  222. package/dist/memory/hybrid-retrieval.d.ts +5 -11
  223. package/dist/memory/hybrid-retrieval.d.ts.map +1 -1
  224. package/dist/memory/hybrid-retrieval.js +78 -58
  225. package/dist/memory/hybrid-retrieval.js.map +1 -1
  226. package/dist/memory/lifecycle.d.ts.map +1 -1
  227. package/dist/memory/lifecycle.js +17 -13
  228. package/dist/memory/lifecycle.js.map +1 -1
  229. package/dist/memory/rebuild-search.d.ts +5 -0
  230. package/dist/memory/rebuild-search.d.ts.map +1 -1
  231. package/dist/memory/rebuild-search.js +101 -76
  232. package/dist/memory/rebuild-search.js.map +1 -1
  233. package/dist/memory/retrieval-query.d.ts +0 -5
  234. package/dist/memory/retrieval-query.d.ts.map +1 -1
  235. package/dist/memory/retrieval-query.js +0 -5
  236. package/dist/memory/retrieval-query.js.map +1 -1
  237. package/dist/memory/retrieval.d.ts.map +1 -1
  238. package/dist/memory/retrieval.js +25 -131
  239. package/dist/memory/retrieval.js.map +1 -1
  240. package/dist/memory/revisions.d.ts +76 -1
  241. package/dist/memory/revisions.d.ts.map +1 -1
  242. package/dist/memory/revisions.js +539 -35
  243. package/dist/memory/revisions.js.map +1 -1
  244. package/dist/memory/scoped-memory.d.ts +16 -1
  245. package/dist/memory/scoped-memory.d.ts.map +1 -1
  246. package/dist/memory/scoped-memory.js +240 -107
  247. package/dist/memory/scoped-memory.js.map +1 -1
  248. package/dist/memory/secrets.d.ts +2 -0
  249. package/dist/memory/secrets.d.ts.map +1 -1
  250. package/dist/memory/secrets.js +36 -0
  251. package/dist/memory/secrets.js.map +1 -1
  252. package/dist/memory/structured-memory.d.ts +3 -2
  253. package/dist/memory/structured-memory.d.ts.map +1 -1
  254. package/dist/memory/structured-memory.js +54 -22
  255. package/dist/memory/structured-memory.js.map +1 -1
  256. package/dist/memory/workspaces.d.ts +9 -0
  257. package/dist/memory/workspaces.d.ts.map +1 -1
  258. package/dist/memory/workspaces.js +67 -0
  259. package/dist/memory/workspaces.js.map +1 -1
  260. package/dist/package-version.d.ts +2 -0
  261. package/dist/package-version.d.ts.map +1 -0
  262. package/dist/package-version.js +7 -0
  263. package/dist/package-version.js.map +1 -0
  264. package/dist/repository/binding.d.ts +5 -0
  265. package/dist/repository/binding.d.ts.map +1 -1
  266. package/dist/repository/binding.js +117 -17
  267. package/dist/repository/binding.js.map +1 -1
  268. package/dist/repository/framework-version.d.ts +8 -0
  269. package/dist/repository/framework-version.d.ts.map +1 -0
  270. package/dist/repository/framework-version.js +29 -0
  271. package/dist/repository/framework-version.js.map +1 -0
  272. package/dist/repository/identity-value.d.ts +4 -0
  273. package/dist/repository/identity-value.d.ts.map +1 -0
  274. package/dist/repository/identity-value.js +24 -0
  275. package/dist/repository/identity-value.js.map +1 -0
  276. package/dist/repository/identity.d.ts.map +1 -1
  277. package/dist/repository/identity.js +2 -0
  278. package/dist/repository/identity.js.map +1 -1
  279. package/dist/repository/project-fingerprint.d.ts +14 -2
  280. package/dist/repository/project-fingerprint.d.ts.map +1 -1
  281. package/dist/repository/project-fingerprint.js +312 -33
  282. package/dist/repository/project-fingerprint.js.map +1 -1
  283. package/dist/repository/remote-url.d.ts +0 -1
  284. package/dist/repository/remote-url.d.ts.map +1 -1
  285. package/dist/repository/remote-url.js +0 -1
  286. package/dist/repository/remote-url.js.map +1 -1
  287. package/dist/runtime-version.d.ts +2 -1
  288. package/dist/runtime-version.d.ts.map +1 -1
  289. package/dist/runtime-version.js +21 -4
  290. package/dist/runtime-version.js.map +1 -1
  291. package/dist/serialization/boundary-json.d.ts +20 -0
  292. package/dist/serialization/boundary-json.d.ts.map +1 -0
  293. package/dist/serialization/boundary-json.js +129 -0
  294. package/dist/serialization/boundary-json.js.map +1 -0
  295. package/dist/serialization/envelope.js +1 -1
  296. package/dist/serialization/envelope.js.map +1 -1
  297. package/dist/serialization/validate.d.ts +7 -0
  298. package/dist/serialization/validate.d.ts.map +1 -1
  299. package/dist/serialization/validate.js +53 -13
  300. package/dist/serialization/validate.js.map +1 -1
  301. package/dist/server/app.js +1 -1
  302. package/dist/server/app.js.map +1 -1
  303. package/dist/server/http.d.ts +4 -3
  304. package/dist/server/http.d.ts.map +1 -1
  305. package/dist/server/http.js +90 -50
  306. package/dist/server/http.js.map +1 -1
  307. package/dist/server/idempotency.d.ts +0 -6
  308. package/dist/server/idempotency.d.ts.map +1 -1
  309. package/dist/server/idempotency.js.map +1 -1
  310. package/dist/server/router.d.ts.map +1 -1
  311. package/dist/server/router.js +21 -4
  312. package/dist/server/router.js.map +1 -1
  313. package/dist/server/routes/agent-capability-gate.d.ts +40 -0
  314. package/dist/server/routes/agent-capability-gate.d.ts.map +1 -0
  315. package/dist/server/routes/agent-capability-gate.js +182 -0
  316. package/dist/server/routes/agent-capability-gate.js.map +1 -0
  317. package/dist/server/routes/agent-events.d.ts.map +1 -1
  318. package/dist/server/routes/agent-events.js +8 -1
  319. package/dist/server/routes/agent-events.js.map +1 -1
  320. package/dist/server/routes/agent-intake.d.ts.map +1 -1
  321. package/dist/server/routes/agent-intake.js +10 -2
  322. package/dist/server/routes/agent-intake.js.map +1 -1
  323. package/dist/server/routes/agent-promotions.d.ts.map +1 -1
  324. package/dist/server/routes/agent-promotions.js +5 -2
  325. package/dist/server/routes/agent-promotions.js.map +1 -1
  326. package/dist/server/routes/agent-runs.d.ts.map +1 -1
  327. package/dist/server/routes/agent-runs.js +18 -4
  328. package/dist/server/routes/agent-runs.js.map +1 -1
  329. package/dist/server/routes/request-binding.d.ts +5 -0
  330. package/dist/server/routes/request-binding.d.ts.map +1 -0
  331. package/dist/server/routes/request-binding.js +49 -0
  332. package/dist/server/routes/request-binding.js.map +1 -0
  333. package/dist/server/routes/task5-support.d.ts +1 -8
  334. package/dist/server/routes/task5-support.d.ts.map +1 -1
  335. package/dist/server/routes/task5-support.js +1 -17
  336. package/dist/server/routes/task5-support.js.map +1 -1
  337. package/dist/server/routes/task5.d.ts.map +1 -1
  338. package/dist/server/routes/task5.js +106 -36
  339. package/dist/server/routes/task5.js.map +1 -1
  340. package/dist/server/write-queue.js +2 -2
  341. package/dist/server/write-queue.js.map +1 -1
  342. package/dist/setup/claude-config.d.ts +2 -1
  343. package/dist/setup/claude-config.d.ts.map +1 -1
  344. package/dist/setup/claude-config.js +54 -4
  345. package/dist/setup/claude-config.js.map +1 -1
  346. package/dist/setup/client-detection.d.ts +7 -2
  347. package/dist/setup/client-detection.d.ts.map +1 -1
  348. package/dist/setup/client-detection.js +27 -9
  349. package/dist/setup/client-detection.js.map +1 -1
  350. package/dist/setup/hermes-config.d.ts +2 -1
  351. package/dist/setup/hermes-config.d.ts.map +1 -1
  352. package/dist/setup/hermes-config.js +53 -12
  353. package/dist/setup/hermes-config.js.map +1 -1
  354. package/dist/setup/legacy-client-cleanup.d.ts +6 -0
  355. package/dist/setup/legacy-client-cleanup.d.ts.map +1 -0
  356. package/dist/setup/legacy-client-cleanup.js +152 -0
  357. package/dist/setup/legacy-client-cleanup.js.map +1 -0
  358. package/dist/setup/managed-text.d.ts.map +1 -1
  359. package/dist/setup/managed-text.js +11 -1
  360. package/dist/setup/managed-text.js.map +1 -1
  361. package/dist/setup/opencode-config.d.ts +2 -1
  362. package/dist/setup/opencode-config.d.ts.map +1 -1
  363. package/dist/setup/opencode-config.js +57 -5
  364. package/dist/setup/opencode-config.js.map +1 -1
  365. package/dist/setup/render.d.ts +2 -1
  366. package/dist/setup/render.d.ts.map +1 -1
  367. package/dist/setup/render.js +91 -17
  368. package/dist/setup/render.js.map +1 -1
  369. package/dist/setup/standard-skills.d.ts +16 -2
  370. package/dist/setup/standard-skills.d.ts.map +1 -1
  371. package/dist/setup/standard-skills.js +37 -13
  372. package/dist/setup/standard-skills.js.map +1 -1
  373. package/dist/setup/strict-json.d.ts +7 -0
  374. package/dist/setup/strict-json.d.ts.map +1 -0
  375. package/dist/setup/strict-json.js +98 -0
  376. package/dist/setup/strict-json.js.map +1 -0
  377. package/dist/setup/strict-toml.d.ts +7 -0
  378. package/dist/setup/strict-toml.d.ts.map +1 -0
  379. package/dist/setup/strict-toml.js +470 -0
  380. package/dist/setup/strict-toml.js.map +1 -0
  381. package/dist/skills/cache.d.ts +15 -0
  382. package/dist/skills/cache.d.ts.map +1 -0
  383. package/dist/skills/cache.js +19 -0
  384. package/dist/skills/cache.js.map +1 -0
  385. package/dist/skills/candidate-ranking.d.ts +15 -0
  386. package/dist/skills/candidate-ranking.d.ts.map +1 -0
  387. package/dist/skills/candidate-ranking.js +52 -0
  388. package/dist/skills/candidate-ranking.js.map +1 -0
  389. package/dist/skills/chunking.d.ts +11 -0
  390. package/dist/skills/chunking.d.ts.map +1 -0
  391. package/dist/skills/chunking.js +291 -0
  392. package/dist/skills/chunking.js.map +1 -0
  393. package/dist/skills/config.d.ts +19 -0
  394. package/dist/skills/config.d.ts.map +1 -0
  395. package/dist/skills/config.js +103 -0
  396. package/dist/skills/config.js.map +1 -0
  397. package/dist/skills/discovery-service.d.ts +26 -0
  398. package/dist/skills/discovery-service.d.ts.map +1 -0
  399. package/dist/skills/discovery-service.js +921 -0
  400. package/dist/skills/discovery-service.js.map +1 -0
  401. package/dist/skills/external-transport.d.ts +2 -0
  402. package/dist/skills/external-transport.d.ts.map +1 -0
  403. package/dist/skills/external-transport.js +30 -0
  404. package/dist/skills/external-transport.js.map +1 -0
  405. package/dist/skills/find.d.ts +16 -0
  406. package/dist/skills/find.d.ts.map +1 -0
  407. package/dist/skills/find.js +58 -0
  408. package/dist/skills/find.js.map +1 -0
  409. package/dist/skills/gap-detection.d.ts +23 -0
  410. package/dist/skills/gap-detection.d.ts.map +1 -0
  411. package/dist/skills/gap-detection.js +62 -0
  412. package/dist/skills/gap-detection.js.map +1 -0
  413. package/dist/skills/import-preparation.d.ts +4 -0
  414. package/dist/skills/import-preparation.d.ts.map +1 -0
  415. package/dist/skills/import-preparation.js +20 -0
  416. package/dist/skills/import-preparation.js.map +1 -0
  417. package/dist/skills/materialization-authority.d.ts +11 -0
  418. package/dist/skills/materialization-authority.d.ts.map +1 -0
  419. package/dist/skills/materialization-authority.js +92 -0
  420. package/dist/skills/materialization-authority.js.map +1 -0
  421. package/dist/skills/materialization-service.d.ts +22 -0
  422. package/dist/skills/materialization-service.d.ts.map +1 -0
  423. package/dist/skills/materialization-service.js +101 -0
  424. package/dist/skills/materialization-service.js.map +1 -0
  425. package/dist/skills/official-catalog.d.ts +31 -0
  426. package/dist/skills/official-catalog.d.ts.map +1 -0
  427. package/dist/skills/official-catalog.js +110 -0
  428. package/dist/skills/official-catalog.js.map +1 -0
  429. package/dist/skills/providers/schema.d.ts +30 -0
  430. package/dist/skills/providers/schema.d.ts.map +1 -0
  431. package/dist/skills/providers/schema.js +354 -0
  432. package/dist/skills/providers/schema.js.map +1 -0
  433. package/dist/skills/providers/skills-sh-compat.d.ts +17 -0
  434. package/dist/skills/providers/skills-sh-compat.d.ts.map +1 -0
  435. package/dist/skills/providers/skills-sh-compat.js +71 -0
  436. package/dist/skills/providers/skills-sh-compat.js.map +1 -0
  437. package/dist/skills/providers/skills-sh-v1.d.ts +23 -0
  438. package/dist/skills/providers/skills-sh-v1.d.ts.map +1 -0
  439. package/dist/skills/providers/skills-sh-v1.js +134 -0
  440. package/dist/skills/providers/skills-sh-v1.js.map +1 -0
  441. package/dist/skills/query-builder.d.ts +18 -0
  442. package/dist/skills/query-builder.d.ts.map +1 -0
  443. package/dist/skills/query-builder.js +93 -0
  444. package/dist/skills/query-builder.js.map +1 -0
  445. package/dist/skills/source/errors.d.ts +9 -0
  446. package/dist/skills/source/errors.d.ts.map +1 -0
  447. package/dist/skills/source/errors.js +15 -0
  448. package/dist/skills/source/errors.js.map +1 -0
  449. package/dist/skills/source/frontmatter.d.ts +9 -0
  450. package/dist/skills/source/frontmatter.d.ts.map +1 -0
  451. package/dist/skills/source/frontmatter.js +77 -0
  452. package/dist/skills/source/frontmatter.js.map +1 -0
  453. package/dist/skills/source/github-fetcher.d.ts +20 -0
  454. package/dist/skills/source/github-fetcher.d.ts.map +1 -0
  455. package/dist/skills/source/github-fetcher.js +292 -0
  456. package/dist/skills/source/github-fetcher.js.map +1 -0
  457. package/dist/skills/source/snapshot-validator.d.ts +28 -0
  458. package/dist/skills/source/snapshot-validator.d.ts.map +1 -0
  459. package/dist/skills/source/snapshot-validator.js +186 -0
  460. package/dist/skills/source/snapshot-validator.js.map +1 -0
  461. package/dist/skills/store.d.ts +181 -0
  462. package/dist/skills/store.d.ts.map +1 -0
  463. package/dist/skills/store.js +1710 -0
  464. package/dist/skills/store.js.map +1 -0
  465. package/dist/skills/types.d.ts +169 -0
  466. package/dist/skills/types.d.ts.map +1 -0
  467. package/dist/skills/types.js +2 -0
  468. package/dist/skills/types.js.map +1 -0
  469. package/dist/web/i18n.d.ts +32 -0
  470. package/dist/web/i18n.d.ts.map +1 -1
  471. package/dist/web/i18n.js +128 -0
  472. package/dist/web/i18n.js.map +1 -1
  473. package/dist/web/server.d.ts.map +1 -1
  474. package/dist/web/server.js +386 -38
  475. package/dist/web/server.js.map +1 -1
  476. package/dist/web/ui.d.ts +4 -0
  477. package/dist/web/ui.d.ts.map +1 -1
  478. package/dist/web/ui.js +382 -6
  479. package/dist/web/ui.js.map +1 -1
  480. package/migrations/009_external_skill_discovery.sql +126 -0
  481. package/package.json +4 -3
  482. package/skills/kiokuko-single-purpose-functions/SKILL.md +288 -0
  483. package/skills/kiokuko-single-purpose-functions/references/kiokuko-patterns.md +344 -0
  484. package/skills/kiokuko-single-purpose-functions/references/review-checklist.md +97 -0
  485. package/skills/kiokuko-ui-design-soul/SKILL.md +1186 -28
  486. package/templates/AGENTS.md +8 -7
  487. package/dist/client/runtime-discovery.d.ts +0 -15
  488. package/dist/client/runtime-discovery.d.ts.map +0 -1
  489. package/dist/client/runtime-discovery.js +0 -45
  490. package/dist/client/runtime-discovery.js.map +0 -1
  491. package/dist/commands/call.d.ts +0 -7
  492. package/dist/commands/call.d.ts.map +0 -1
  493. package/dist/commands/call.js +0 -10
  494. package/dist/commands/call.js.map +0 -1
  495. package/dist/commands/link.d.ts +0 -6
  496. package/dist/commands/link.d.ts.map +0 -1
  497. package/dist/commands/link.js +0 -6
  498. package/dist/commands/link.js.map +0 -1
  499. package/dist/commands/promote.d.ts +0 -6
  500. package/dist/commands/promote.d.ts.map +0 -1
  501. package/dist/commands/promote.js +0 -6
  502. package/dist/commands/promote.js.map +0 -1
  503. package/dist/commands/read.d.ts +0 -6
  504. package/dist/commands/read.d.ts.map +0 -1
  505. package/dist/commands/read.js +0 -4
  506. package/dist/commands/read.js.map +0 -1
  507. package/dist/commands/recall.d.ts +0 -6
  508. package/dist/commands/recall.d.ts.map +0 -1
  509. package/dist/commands/recall.js +0 -4
  510. package/dist/commands/recall.js.map +0 -1
  511. package/dist/commands/record.d.ts +0 -6
  512. package/dist/commands/record.d.ts.map +0 -1
  513. package/dist/commands/record.js +0 -4
  514. package/dist/commands/record.js.map +0 -1
  515. package/dist/commands/search.d.ts +0 -6
  516. package/dist/commands/search.d.ts.map +0 -1
  517. package/dist/commands/search.js +0 -4
  518. package/dist/commands/search.js.map +0 -1
  519. package/dist/commands/supersede.d.ts +0 -6
  520. package/dist/commands/supersede.d.ts.map +0 -1
  521. package/dist/commands/supersede.js +0 -6
  522. package/dist/commands/supersede.js.map +0 -1
  523. package/dist/knowledge/sources.d.ts +0 -72
  524. package/dist/knowledge/sources.d.ts.map +0 -1
  525. package/dist/knowledge/sources.js +0 -388
  526. package/dist/knowledge/sources.js.map +0 -1
  527. package/dist/setup/opencode-evidence.d.ts +0 -37
  528. package/dist/setup/opencode-evidence.d.ts.map +0 -1
  529. package/dist/setup/opencode-evidence.js +0 -58
  530. package/dist/setup/opencode-evidence.js.map +0 -1
  531. package/dist/setup/opencode-loop-guard.d.ts +0 -9
  532. package/dist/setup/opencode-loop-guard.d.ts.map +0 -1
  533. package/dist/setup/opencode-loop-guard.js +0 -248
  534. package/dist/setup/opencode-loop-guard.js.map +0 -1
@@ -1,74 +1,369 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { readFile } from 'node:fs/promises';
3
- import { withImmediateTransaction } from '../db/transaction.js';
2
+ import { constants } from 'node:fs';
3
+ import { lstat, open } from 'node:fs/promises';
4
+ import { TextDecoder } from 'node:util';
5
+ import { withDeferredReadTransaction, withImmediateTransaction } from '../db/transaction.js';
4
6
  import { KiokukoError } from '../errors.js';
5
- import { findSecret } from '../memory/secrets.js';
7
+ import { findSecretInValue } from '../memory/secrets.js';
8
+ import { readEntry } from '../memory/entries.js';
6
9
  import { insertEntryRevisionInTransaction } from '../memory/revisions.js';
7
10
  import { syncEntrySearchProjection } from '../memory/structured-memory.js';
8
- import { canonicalContentHash, canonicalJson, requireWorkspace, validateRecordInput, } from '../serialization/validate.js';
11
+ import { isWellFormedUnicode } from '../serialization/boundary-json.js';
12
+ import { canonicalEntryRevisionContentHash, canonicalJson, requireWorkspace, validateRecordInput, } from '../serialization/validate.js';
13
+ import { parseStrictJson } from '../setup/strict-json.js';
14
+ export const WORKSPACE_ARCHIVE_MAX_BYTES = 64 * 1024 * 1024;
15
+ export const WORKSPACE_ARCHIVE_MAX_LINES = 10_000;
16
+ export const WORKSPACE_ARCHIVE_MAX_LINE_BYTES = 512 * 1024;
9
17
  const RELATIONS = new Set(['supports', 'contradicts', 'derived_from', 'related_to']);
10
18
  const AUDIT_OPERATIONS = new Set(['record', 'promote', 'supersede', 'link', 'import', 'purge']);
19
+ const HASH_PATTERN = /^[0-9a-f]{64}$/u;
20
+ const TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u;
21
+ const RECORD_FIELDS = {
22
+ checksum: ['type', 'sha256'],
23
+ manifest: ['type', 'apiVersion', 'workspace', 'format', 'version', 'counts'],
24
+ entry: [
25
+ 'type', 'id', 'workspace', 'kind', 'status', 'title', 'body', 'summary',
26
+ 'scope_json', 'provenance_json', 'trust_level', 'confidence', 'content_hash',
27
+ 'revision', 'superseded_by', 'created_by', 'created_at', 'updated_at', 'verified_at',
28
+ ],
29
+ tag: ['type', 'entry_id', 'tag'],
30
+ link: ['type', 'from_entry_id', 'to_entry_id', 'relation', 'created_at', 'created_by'],
31
+ audit: ['type', 'event_id', 'entry_id', 'workspace', 'operation', 'actor', 'details_json', 'created_at'],
32
+ };
33
+ const COUNT_FIELDS = ['entries', 'tags', 'links', 'audit'];
34
+ const STRICT_JSON_OPTIONS = {
35
+ allowTrailingComma: false,
36
+ disallowComments: true,
37
+ allowEmptyContent: false,
38
+ };
39
+ function archiveChanged() {
40
+ return new KiokukoError('CONFLICT', 'Import input changed while it was being read');
41
+ }
42
+ function isSameArchiveFileState(left, right) {
43
+ return left.isFile()
44
+ && right.isFile()
45
+ && left.dev === right.dev
46
+ && left.ino === right.ino
47
+ && left.mode === right.mode
48
+ && left.nlink === right.nlink
49
+ && left.uid === right.uid
50
+ && left.gid === right.gid
51
+ && left.rdev === right.rdev
52
+ && left.size === right.size
53
+ && left.mtimeNs === right.mtimeNs
54
+ && left.ctimeNs === right.ctimeNs;
55
+ }
56
+ function assertBindableArchiveFile(state) {
57
+ if (!state.isFile())
58
+ throw new KiokukoError('VALIDATION_ERROR', 'Import input must be a regular file');
59
+ if (state.ino === 0n) {
60
+ throw new KiokukoError('SECURITY_REJECTION', 'Import input does not expose a stable file identity');
61
+ }
62
+ }
63
+ function archiveOpenFlags() {
64
+ if (process.platform === 'win32')
65
+ return constants.O_RDONLY;
66
+ if (!Number.isSafeInteger(constants.O_NOFOLLOW) || constants.O_NOFOLLOW <= 0
67
+ || !Number.isSafeInteger(constants.O_NONBLOCK) || constants.O_NONBLOCK <= 0) {
68
+ throw new KiokukoError('SECURITY_REJECTION', 'This platform cannot safely open an import archive');
69
+ }
70
+ return constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK;
71
+ }
72
+ async function readBoundedArchive(filePath, dependencies) {
73
+ const planned = await lstat(filePath, { bigint: true });
74
+ assertBindableArchiveFile(planned);
75
+ if (planned.size > BigInt(WORKSPACE_ARCHIVE_MAX_BYTES)) {
76
+ throw new KiokukoError('VALIDATION_ERROR', `Workspace archive exceeds the ${WORKSPACE_ARCHIVE_MAX_BYTES}-byte maximum`);
77
+ }
78
+ await dependencies.afterInputPlanned?.(filePath);
79
+ const handle = await open(filePath, archiveOpenFlags());
80
+ let result;
81
+ let operationError;
82
+ try {
83
+ const initial = await handle.stat({ bigint: true });
84
+ assertBindableArchiveFile(initial);
85
+ if (!isSameArchiveFileState(planned, initial))
86
+ throw archiveChanged();
87
+ await dependencies.afterInputBound?.(filePath);
88
+ if (initial.size > BigInt(WORKSPACE_ARCHIVE_MAX_BYTES)) {
89
+ throw new KiokukoError('VALIDATION_ERROR', `Workspace archive exceeds the ${WORKSPACE_ARCHIVE_MAX_BYTES}-byte maximum`);
90
+ }
91
+ const expectedSize = Number(initial.size);
92
+ const buffer = Buffer.alloc(expectedSize);
93
+ let offset = 0;
94
+ while (offset < expectedSize) {
95
+ const { bytesRead } = await handle.read(buffer, offset, expectedSize - offset, offset);
96
+ if (bytesRead === 0)
97
+ throw archiveChanged();
98
+ offset += bytesRead;
99
+ }
100
+ const extra = Buffer.allocUnsafe(1);
101
+ const { bytesRead: extraBytesRead } = await handle.read(extra, 0, 1, expectedSize);
102
+ if (extraBytesRead !== 0)
103
+ throw archiveChanged();
104
+ await dependencies.afterInputRead?.(filePath);
105
+ const final = await handle.stat({ bigint: true });
106
+ if (!isSameArchiveFileState(initial, final))
107
+ throw archiveChanged();
108
+ const finalPath = await lstat(filePath, { bigint: true });
109
+ if (!isSameArchiveFileState(final, finalPath))
110
+ throw archiveChanged();
111
+ result = buffer;
112
+ }
113
+ catch (error) {
114
+ operationError = error;
115
+ }
116
+ try {
117
+ await handle.close();
118
+ }
119
+ catch (closeError) {
120
+ if (operationError !== undefined) {
121
+ throw new AggregateError([operationError, closeError], 'Import read failed and the input file could not be closed');
122
+ }
123
+ throw closeError;
124
+ }
125
+ if (operationError !== undefined)
126
+ throw operationError;
127
+ if (result === undefined)
128
+ throw new KiokukoError('INTEGRITY_ERROR', 'Import read produced no result');
129
+ return result;
130
+ }
131
+ export function validateWorkspaceArchiveByteLayout(bytes) {
132
+ if (bytes.byteLength > WORKSPACE_ARCHIVE_MAX_BYTES) {
133
+ throw new KiokukoError('VALIDATION_ERROR', `Workspace archive exceeds the ${WORKSPACE_ARCHIVE_MAX_BYTES}-byte maximum`);
134
+ }
135
+ let lineBytes = 0;
136
+ let lines = 0;
137
+ for (const byte of bytes) {
138
+ if (byte === 0x0a) {
139
+ lines += 1;
140
+ lineBytes = 0;
141
+ }
142
+ else {
143
+ lineBytes += 1;
144
+ if (lineBytes > WORKSPACE_ARCHIVE_MAX_LINE_BYTES) {
145
+ throw new KiokukoError('VALIDATION_ERROR', `Workspace archive line exceeds the ${WORKSPACE_ARCHIVE_MAX_LINE_BYTES}-byte maximum`);
146
+ }
147
+ }
148
+ if (lines > WORKSPACE_ARCHIVE_MAX_LINES) {
149
+ throw new KiokukoError('VALIDATION_ERROR', `Workspace archive exceeds the ${WORKSPACE_ARCHIVE_MAX_LINES}-line maximum`);
150
+ }
151
+ }
152
+ if (lineBytes > 0)
153
+ lines += 1;
154
+ if (lines > WORKSPACE_ARCHIVE_MAX_LINES) {
155
+ throw new KiokukoError('VALIDATION_ERROR', `Workspace archive exceeds the ${WORKSPACE_ARCHIVE_MAX_LINES}-line maximum`);
156
+ }
157
+ }
11
158
  function objectValue(value, label) {
12
159
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
13
160
  throw new KiokukoError('VALIDATION_ERROR', `${label} must be a JSON object`);
14
161
  }
15
162
  return value;
16
163
  }
164
+ function exactFields(value, fields, label) {
165
+ const actual = Object.keys(value);
166
+ const allowed = new Set(fields);
167
+ if (actual.length !== fields.length || actual.some((field) => !allowed.has(field))) {
168
+ throw new KiokukoError('VALIDATION_ERROR', `${label} has invalid fields`);
169
+ }
170
+ }
171
+ function rejectSecretValue(value) {
172
+ const finding = findSecretInValue(value);
173
+ if (finding !== undefined) {
174
+ throw new KiokukoError('SECURITY_REJECTION', 'Import contains secret-like content', { kind: finding.kind });
175
+ }
176
+ }
17
177
  function stringValue(value, label, allowEmpty = false) {
18
178
  if (typeof value !== 'string' || (!allowEmpty && value.length === 0)) {
19
179
  throw new KiokukoError('VALIDATION_ERROR', `${label} must be a ${allowEmpty ? '' : 'non-empty '}string`);
20
180
  }
181
+ if (!isWellFormedUnicode(value)) {
182
+ throw new KiokukoError('VALIDATION_ERROR', `${label} must contain well-formed Unicode`);
183
+ }
21
184
  return value;
22
185
  }
186
+ function inputPathValue(value) {
187
+ const filePath = stringValue(value, 'input');
188
+ if (/\p{Cc}/u.test(filePath)) {
189
+ throw new KiokukoError('VALIDATION_ERROR', 'input must not contain control characters');
190
+ }
191
+ return filePath;
192
+ }
23
193
  function nullableString(value, label) {
24
- if (value === null || value === undefined)
194
+ if (value === null)
25
195
  return null;
26
196
  return stringValue(value, label, true);
27
197
  }
28
198
  function parsedJsonObject(value, label) {
29
- if (typeof value === 'object' && value !== null && !Array.isArray(value))
30
- return value;
31
- const parsed = JSON.parse(stringValue(value, label));
32
- return objectValue(parsed, label);
199
+ const text = stringValue(value, label);
200
+ const parsed = parseStrictJson(text, STRICT_JSON_OPTIONS, `${label} must contain valid JSON`);
201
+ const object = objectValue(parsed, label);
202
+ rejectSecretValue(object);
203
+ if (canonicalJson(object) !== text) {
204
+ throw new KiokukoError('VALIDATION_ERROR', `${label} must use canonical JSON encoding`);
205
+ }
206
+ return object;
33
207
  }
34
- function parseJson(text) {
35
- try {
36
- return objectValue(JSON.parse(text), 'JSON line');
208
+ function numberValue(value, label) {
209
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
210
+ throw new KiokukoError('VALIDATION_ERROR', `${label} must be a finite number`);
37
211
  }
38
- catch (error) {
39
- if (error instanceof KiokukoError)
40
- throw error;
41
- throw new KiokukoError('VALIDATION_ERROR', 'Import contains invalid JSON');
212
+ return value;
213
+ }
214
+ function integerValue(value, label, minimum = 0) {
215
+ if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < minimum) {
216
+ throw new KiokukoError('VALIDATION_ERROR', `${label} must be an integer greater than or equal to ${minimum}`);
217
+ }
218
+ return value;
219
+ }
220
+ function timestampValue(value, label) {
221
+ const timestamp = stringValue(value, label);
222
+ const parsed = Date.parse(timestamp);
223
+ if (!TIMESTAMP_PATTERN.test(timestamp) || !Number.isFinite(parsed) || new Date(parsed).toISOString() !== timestamp) {
224
+ throw new KiokukoError('VALIDATION_ERROR', `${label} must be a canonical ISO-8601 UTC timestamp`);
225
+ }
226
+ return timestamp;
227
+ }
228
+ function hashValue(value, label) {
229
+ const hash = stringValue(value, label);
230
+ if (!HASH_PATTERN.test(hash))
231
+ throw new KiokukoError('VALIDATION_ERROR', `${label} must be a lowercase SHA-256 hash`);
232
+ return hash;
233
+ }
234
+ function parseJsonLine(text, label) {
235
+ const parsed = parseStrictJson(text, STRICT_JSON_OPTIONS, `${label} contains invalid JSON`);
236
+ const object = objectValue(parsed, label);
237
+ if (canonicalJson(object) !== text) {
238
+ throw new KiokukoError('VALIDATION_ERROR', `${label} must use canonical JSON encoding`);
42
239
  }
240
+ return object;
241
+ }
242
+ function validateEntryRecord(record, workspace) {
243
+ if (record.type !== 'entry')
244
+ throw new KiokukoError('VALIDATION_ERROR', 'Entry record type is invalid');
245
+ stringValue(record.id, 'entry.id');
246
+ if (stringValue(record.workspace, 'entry.workspace') !== workspace) {
247
+ throw new KiokukoError('VALIDATION_ERROR', 'Entry workspace does not match manifest workspace');
248
+ }
249
+ stringValue(record.kind, 'entry.kind');
250
+ stringValue(record.status, 'entry.status');
251
+ stringValue(record.title, 'entry.title');
252
+ stringValue(record.body, 'entry.body', true);
253
+ nullableString(record.summary, 'entry.summary');
254
+ parsedJsonObject(record.scope_json, 'entry.scope_json');
255
+ parsedJsonObject(record.provenance_json, 'entry.provenance_json');
256
+ stringValue(record.trust_level, 'entry.trust_level');
257
+ numberValue(record.confidence, 'entry.confidence');
258
+ hashValue(record.content_hash, 'entry.content_hash');
259
+ if (integerValue(record.revision, 'entry.revision', 1) !== 1) {
260
+ throw new KiokukoError('VALIDATION_ERROR', 'Workspace archive v2 can import only revision 1 snapshots; higher revisions require a history-preserving archive format');
261
+ }
262
+ nullableString(record.superseded_by, 'entry.superseded_by');
263
+ stringValue(record.created_by, 'entry.created_by');
264
+ timestampValue(record.created_at, 'entry.created_at');
265
+ timestampValue(record.updated_at, 'entry.updated_at');
266
+ if (record.verified_at !== null)
267
+ timestampValue(record.verified_at, 'entry.verified_at');
268
+ }
269
+ function validateTagRecord(record) {
270
+ if (record.type !== 'tag')
271
+ throw new KiokukoError('VALIDATION_ERROR', 'Tag record type is invalid');
272
+ stringValue(record.entry_id, 'tag.entry_id');
273
+ stringValue(record.tag, 'tag.tag');
274
+ }
275
+ function validateLinkRecord(record) {
276
+ if (record.type !== 'link')
277
+ throw new KiokukoError('VALIDATION_ERROR', 'Link record type is invalid');
278
+ stringValue(record.from_entry_id, 'link.from_entry_id');
279
+ stringValue(record.to_entry_id, 'link.to_entry_id');
280
+ stringValue(record.relation, 'link.relation');
281
+ timestampValue(record.created_at, 'link.created_at');
282
+ stringValue(record.created_by, 'link.created_by');
283
+ }
284
+ function validateAuditRecord(record, workspace) {
285
+ if (record.type !== 'audit')
286
+ throw new KiokukoError('VALIDATION_ERROR', 'Audit record type is invalid');
287
+ stringValue(record.event_id, 'audit.event_id');
288
+ nullableString(record.entry_id, 'audit.entry_id');
289
+ if (stringValue(record.workspace, 'audit.workspace') !== workspace) {
290
+ throw new KiokukoError('VALIDATION_ERROR', 'Audit workspace does not match manifest workspace');
291
+ }
292
+ stringValue(record.operation, 'audit.operation');
293
+ stringValue(record.actor, 'audit.actor');
294
+ parsedJsonObject(record.details_json, 'audit.details_json');
295
+ timestampValue(record.created_at, 'audit.created_at');
43
296
  }
44
297
  function parseImport(text) {
45
- const lines = text.split('\n');
46
- while (lines.at(-1) === '')
47
- lines.pop();
48
- if (lines.length < 2)
49
- throw new KiokukoError('VALIDATION_ERROR', 'Import file is empty or missing manifest');
50
- const checksumLine = parseJson(lines[0]);
51
- if (checksumLine.type !== 'checksum' || typeof checksumLine.sha256 !== 'string') {
298
+ if (!text.endsWith('\n'))
299
+ throw new KiokukoError('VALIDATION_ERROR', 'Import must end with exactly one newline');
300
+ const lines = text.slice(0, -1).split('\n');
301
+ if (lines.length < 2 || lines.some((line) => line.length === 0)) {
302
+ throw new KiokukoError('VALIDATION_ERROR', 'Import file is empty, truncated, or contains blank lines');
303
+ }
304
+ const checksumLine = parseJsonLine(lines[0], 'Import checksum line');
305
+ exactFields(checksumLine, RECORD_FIELDS.checksum, 'Import checksum line');
306
+ if (checksumLine.type !== 'checksum') {
52
307
  throw new KiokukoError('VALIDATION_ERROR', 'Import checksum line is invalid');
53
308
  }
309
+ const checksum = hashValue(checksumLine.sha256, 'Import checksum');
54
310
  const payload = `${lines.slice(1).join('\n')}\n`;
55
311
  const actual = createHash('sha256').update(payload, 'utf8').digest('hex');
56
- if (actual !== checksumLine.sha256)
312
+ if (actual !== checksum)
57
313
  throw new KiokukoError('INTEGRITY_ERROR', 'Import checksum mismatch');
58
- const payloadLines = lines.slice(1).map(parseJson);
59
- const manifest = payloadLines.find((line) => line.type === 'manifest');
60
- if (!manifest || typeof manifest.workspace !== 'string')
61
- throw new KiokukoError('VALIDATION_ERROR', 'Import manifest is invalid');
62
- if (manifest.format !== 'kiokuko-jsonl' || manifest.apiVersion !== '1') {
314
+ const payloadLines = lines.slice(1).map((line, index) => parseJsonLine(line, `Import payload line ${index + 1}`));
315
+ const manifest = payloadLines[0];
316
+ exactFields(manifest, RECORD_FIELDS.manifest, 'Import manifest');
317
+ if (manifest.format !== 'kiokuko-jsonl' || manifest.apiVersion !== '1' || manifest.version !== 2) {
63
318
  throw new KiokukoError('VALIDATION_ERROR', 'Unsupported import format');
64
319
  }
65
- const entries = payloadLines.filter((line) => line.type === 'entry');
66
- const tags = payloadLines.filter((line) => line.type === 'tag');
67
- const links = payloadLines.filter((line) => line.type === 'link');
68
- const audit = payloadLines.filter((line) => line.type === 'audit');
69
- const counts = objectValue(manifest.counts ?? {}, 'manifest.counts');
320
+ if (manifest.type !== 'manifest')
321
+ throw new KiokukoError('VALIDATION_ERROR', 'Import manifest is invalid');
322
+ rejectSecretValue(manifest);
323
+ const workspace = requireWorkspace(manifest.workspace);
324
+ const counts = objectValue(manifest.counts, 'manifest.counts');
325
+ exactFields(counts, COUNT_FIELDS, 'manifest.counts');
326
+ for (const field of COUNT_FIELDS)
327
+ integerValue(counts[field], `manifest.counts.${field}`);
328
+ const entries = [];
329
+ const tags = [];
330
+ const links = [];
331
+ const audit = [];
332
+ for (const line of payloadLines.slice(1)) {
333
+ if (line.type === 'manifest' || line.type === 'checksum') {
334
+ throw new KiokukoError('INTEGRITY_ERROR', `Import contains a duplicate ${String(line.type)} record`);
335
+ }
336
+ switch (line.type) {
337
+ case 'entry':
338
+ exactFields(line, RECORD_FIELDS.entry, 'Import entry record');
339
+ validateEntryRecord(line, workspace);
340
+ rejectSecretValue(line);
341
+ entries.push(line);
342
+ break;
343
+ case 'tag':
344
+ exactFields(line, RECORD_FIELDS.tag, 'Import tag record');
345
+ validateTagRecord(line);
346
+ rejectSecretValue(line);
347
+ tags.push(line);
348
+ break;
349
+ case 'link':
350
+ exactFields(line, RECORD_FIELDS.link, 'Import link record');
351
+ validateLinkRecord(line);
352
+ rejectSecretValue(line);
353
+ links.push(line);
354
+ break;
355
+ case 'audit':
356
+ exactFields(line, RECORD_FIELDS.audit, 'Import audit record');
357
+ validateAuditRecord(line, workspace);
358
+ rejectSecretValue(line);
359
+ audit.push(line);
360
+ break;
361
+ default:
362
+ throw new KiokukoError('VALIDATION_ERROR', `Import contains unsupported record type ${String(line.type)}`);
363
+ }
364
+ }
70
365
  for (const [key, actualCount] of Object.entries({ entries, tags, links, audit })) {
71
- if (counts[key] !== undefined && counts[key] !== actualCount.length) {
366
+ if (counts[key] !== actualCount.length) {
72
367
  throw new KiokukoError('INTEGRITY_ERROR', `Import ${key} count does not match manifest`);
73
368
  }
74
369
  }
@@ -84,15 +379,15 @@ function importedEntry(raw, workspace) {
84
379
  const scope = parsedJsonObject(raw.scope_json, 'entry.scope_json');
85
380
  const provenance = parsedJsonObject(raw.provenance_json, 'entry.provenance_json');
86
381
  const trustLevel = stringValue(raw.trust_level, 'entry.trust_level');
87
- const confidence = Number(raw.confidence);
88
- const tags = Array.isArray(raw.tags) ? raw.tags : [];
382
+ const confidence = numberValue(raw.confidence, 'entry.confidence');
383
+ const persistedTags = Array.isArray(raw.tags) ? raw.tags : [];
89
384
  const createdBy = stringValue(raw.created_by, 'entry.created_by');
90
385
  const supersededBy = nullableString(raw.superseded_by, 'entry.superseded_by');
91
- const createdAt = stringValue(raw.created_at, 'entry.created_at');
92
- const updatedAt = stringValue(raw.updated_at, 'entry.updated_at');
93
- const verifiedAt = nullableString(raw.verified_at, 'entry.verified_at');
94
- const revision = Number(raw.revision);
95
- const contentHash = stringValue(raw.content_hash, 'entry.content_hash');
386
+ const createdAt = timestampValue(raw.created_at, 'entry.created_at');
387
+ const updatedAt = timestampValue(raw.updated_at, 'entry.updated_at');
388
+ const verifiedAt = raw.verified_at === null ? null : timestampValue(raw.verified_at, 'entry.verified_at');
389
+ const revision = integerValue(raw.revision, 'entry.revision', 1);
390
+ const contentHash = hashValue(raw.content_hash, 'entry.content_hash');
96
391
  const validated = validateRecordInput({
97
392
  workspace,
98
393
  kind,
@@ -104,14 +399,26 @@ function importedEntry(raw, workspace) {
104
399
  provenance,
105
400
  trustLevel,
106
401
  confidence,
107
- tags,
402
+ tags: persistedTags,
108
403
  createdBy,
109
404
  });
110
- if (status === 'superseded' && !supersededBy)
111
- throw new KiokukoError('VALIDATION_ERROR', 'Superseded entry needs superseded_by');
112
- if (!Number.isInteger(revision) || revision < 1)
113
- throw new KiokukoError('VALIDATION_ERROR', 'entry.revision must be a positive integer');
114
- const expectedHash = canonicalContentHash({
405
+ if ((status === 'superseded') !== (supersededBy !== null)) {
406
+ throw new KiokukoError('VALIDATION_ERROR', 'entry.status and entry.superseded_by are inconsistent');
407
+ }
408
+ if (status === 'candidate' && verifiedAt !== null) {
409
+ throw new KiokukoError('VALIDATION_ERROR', 'Candidate entries cannot have entry.verified_at');
410
+ }
411
+ if (status === 'verified' && verifiedAt === null) {
412
+ throw new KiokukoError('VALIDATION_ERROR', 'Verified entries require entry.verified_at');
413
+ }
414
+ if (createdAt > updatedAt
415
+ || verifiedAt !== null && (verifiedAt < createdAt || verifiedAt > updatedAt)) {
416
+ throw new KiokukoError('VALIDATION_ERROR', 'Entry lifecycle timestamps are inconsistent');
417
+ }
418
+ if (revision !== 1) {
419
+ throw new KiokukoError('VALIDATION_ERROR', 'Workspace archive v2 can import only revision 1 snapshots; higher revisions require a history-preserving archive format');
420
+ }
421
+ const hashInput = {
115
422
  kind: validated.kind,
116
423
  title: validated.title,
117
424
  body: validated.body,
@@ -119,19 +426,15 @@ function importedEntry(raw, workspace) {
119
426
  scope: validated.scope,
120
427
  provenance: validated.provenance,
121
428
  tags: validated.tags,
122
- });
123
- if (expectedHash !== contentHash)
429
+ };
430
+ const expectedHash = canonicalEntryRevisionContentHash(hashInput);
431
+ if (expectedHash !== contentHash) {
124
432
  throw new KiokukoError('INTEGRITY_ERROR', `Content hash mismatch for entry ${id}`);
125
- const secretFinding = findSecret(canonicalJson({
126
- title: validated.title,
127
- body: validated.body,
128
- summary: validated.summary,
129
- scope: validated.scope,
130
- provenance: validated.provenance,
131
- tags: validated.tags,
132
- }));
133
- if (secretFinding)
134
- throw new KiokukoError('SECURITY_REJECTION', 'Import contains secret-like content', { kind: secretFinding.kind });
433
+ }
434
+ if (persistedTags.length !== validated.tags.length
435
+ || persistedTags.some((tag, index) => tag !== validated.tags[index])) {
436
+ throw new KiokukoError('VALIDATION_ERROR', `Tags for entry ${id} are not in canonical archive order`);
437
+ }
135
438
  return {
136
439
  id,
137
440
  workspace,
@@ -140,8 +443,8 @@ function importedEntry(raw, workspace) {
140
443
  title: validated.title,
141
444
  body: validated.body,
142
445
  summary: validated.summary,
143
- scopeJson: canonicalJson(validated.scope),
144
- provenanceJson: canonicalJson(validated.provenance),
446
+ scope: validated.scope,
447
+ provenance: validated.provenance,
145
448
  trustLevel: validated.trustLevel,
146
449
  confidence: validated.confidence,
147
450
  contentHash,
@@ -154,13 +457,44 @@ function importedEntry(raw, workspace) {
154
457
  tags: validated.tags,
155
458
  };
156
459
  }
460
+ function validateSupersessionGraph(entries) {
461
+ const replacements = new Map(entries.map((entry) => [entry.id, entry.supersededBy]));
462
+ const state = new Map();
463
+ for (const entry of entries) {
464
+ if (state.get(entry.id) === 'complete')
465
+ continue;
466
+ const path = [];
467
+ let current = entry.id;
468
+ while (current !== null) {
469
+ if (!replacements.has(current)) {
470
+ throw new KiokukoError('VALIDATION_ERROR', `Supersession references unknown entry ${current}`);
471
+ }
472
+ const currentState = state.get(current);
473
+ if (currentState === 'visiting')
474
+ throw new KiokukoError('VALIDATION_ERROR', 'Import contains a supersession cycle');
475
+ if (currentState === 'complete')
476
+ break;
477
+ state.set(current, 'visiting');
478
+ path.push(current);
479
+ current = replacements.get(current) ?? null;
480
+ }
481
+ for (const visited of path)
482
+ state.set(visited, 'complete');
483
+ }
484
+ }
157
485
  function validateRelatedLines(document, entryIds) {
486
+ const seenTags = new Set();
158
487
  for (const tag of document.tags) {
159
488
  const entryId = stringValue(tag.entry_id, 'tag.entry_id');
160
- stringValue(tag.tag, 'tag.tag');
489
+ const value = stringValue(tag.tag, 'tag.tag');
161
490
  if (!entryIds.has(entryId))
162
491
  throw new KiokukoError('VALIDATION_ERROR', `Tag references unknown entry ${entryId}`);
492
+ const identity = `${entryId}\u0000${value}`;
493
+ if (seenTags.has(identity))
494
+ throw new KiokukoError('VALIDATION_ERROR', 'Import contains a duplicate tag record');
495
+ seenTags.add(identity);
163
496
  }
497
+ const seenLinks = new Set();
164
498
  for (const link of document.links) {
165
499
  const from = stringValue(link.from_entry_id, 'link.from_entry_id');
166
500
  const to = stringValue(link.to_entry_id, 'link.to_entry_id');
@@ -168,7 +502,12 @@ function validateRelatedLines(document, entryIds) {
168
502
  if (!entryIds.has(from) || !entryIds.has(to) || !RELATIONS.has(relation)) {
169
503
  throw new KiokukoError('VALIDATION_ERROR', 'Import contains an invalid link');
170
504
  }
505
+ const identity = `${from}\u0000${to}\u0000${relation}`;
506
+ if (seenLinks.has(identity))
507
+ throw new KiokukoError('VALIDATION_ERROR', 'Import contains a duplicate link record');
508
+ seenLinks.add(identity);
171
509
  }
510
+ const seenAuditEvents = new Set();
172
511
  for (const event of document.audit) {
173
512
  const entryId = nullableString(event.entry_id, 'audit.entry_id');
174
513
  const operation = stringValue(event.operation, 'audit.operation');
@@ -176,25 +515,157 @@ function validateRelatedLines(document, entryIds) {
176
515
  throw new KiokukoError('VALIDATION_ERROR', `Audit references unknown entry ${entryId}`);
177
516
  if (!AUDIT_OPERATIONS.has(operation))
178
517
  throw new KiokukoError('VALIDATION_ERROR', `Unsupported audit operation ${operation}`);
179
- stringValue(event.event_id, 'audit.event_id');
518
+ const eventId = stringValue(event.event_id, 'audit.event_id');
519
+ if (seenAuditEvents.has(eventId))
520
+ throw new KiokukoError('VALIDATION_ERROR', 'Import contains a duplicate audit event');
521
+ seenAuditEvents.add(eventId);
180
522
  stringValue(event.workspace, 'audit.workspace');
181
523
  stringValue(event.actor, 'audit.actor');
182
524
  stringValue(event.details_json, 'audit.details_json', true);
183
525
  stringValue(event.created_at, 'audit.created_at');
184
526
  }
185
527
  }
186
- function existingEntry(database, id) {
187
- return database.prepare(`
188
- SELECT e.id, r.content_hash
189
- FROM entries AS e
190
- JOIN entry_revisions AS r ON r.entry_id = e.id AND r.revision = e.current_revision
191
- WHERE e.id = ?
192
- `).get(id);
528
+ function sameStringArray(left, right) {
529
+ return left.length === right.length && left.every((value, index) => value === right[index]);
530
+ }
531
+ function entryMatchesArchive(existing, imported) {
532
+ return existing.id === imported.id
533
+ && existing.workspace === imported.workspace
534
+ && existing.kind === imported.kind
535
+ && existing.status === imported.status
536
+ && existing.title === imported.title
537
+ && existing.body === imported.body
538
+ && existing.summary === imported.summary
539
+ && canonicalJson(existing.scope) === canonicalJson(imported.scope)
540
+ && canonicalJson(existing.provenance) === canonicalJson(imported.provenance)
541
+ && existing.trustLevel === imported.trustLevel
542
+ && existing.confidence === imported.confidence
543
+ && existing.contentHash === imported.contentHash
544
+ && existing.revision === imported.revision
545
+ && existing.supersededBy === imported.supersededBy
546
+ && existing.createdBy === imported.createdBy
547
+ && existing.createdAt === imported.createdAt
548
+ && existing.updatedAt === imported.updatedAt
549
+ && existing.verifiedAt === imported.verifiedAt
550
+ && sameStringArray(existing.tags, imported.tags);
551
+ }
552
+ function planEntryImport(database, entries, workspace) {
553
+ const newEntries = [];
554
+ const duplicateIds = new Set();
555
+ if (database === undefined) {
556
+ return { newEntries: [...entries], duplicates: 0 };
557
+ }
558
+ for (const entry of entries) {
559
+ const owner = database.prepare('SELECT workspace FROM entries WHERE id = ?').get(entry.id);
560
+ if (owner !== undefined) {
561
+ if (typeof owner.workspace !== 'string')
562
+ throw new KiokukoError('INTEGRITY_ERROR', 'Stored entry workspace is invalid');
563
+ if (owner.workspace !== workspace) {
564
+ throw new KiokukoError('CONFLICT', `Entry ID ${entry.id} already belongs to another workspace`);
565
+ }
566
+ const existing = readEntry(database, { workspace, entryId: entry.id });
567
+ if (existing.contentHash !== entry.contentHash) {
568
+ throw new KiokukoError('CONFLICT', `Entry ID ${entry.id} already contains different content`);
569
+ }
570
+ duplicateIds.add(entry.id);
571
+ continue;
572
+ }
573
+ const hashMatch = database.prepare(`
574
+ SELECT entry_id
575
+ FROM entry_revisions
576
+ WHERE workspace = ? AND content_hash = ?
577
+ `).get(workspace, entry.contentHash);
578
+ if (hashMatch !== undefined) {
579
+ if (typeof hashMatch.entry_id !== 'string') {
580
+ throw new KiokukoError('INTEGRITY_ERROR', 'Stored content identity is invalid');
581
+ }
582
+ if (hashMatch.entry_id === entry.id) {
583
+ throw new KiokukoError('INTEGRITY_ERROR', 'Stored revision has no owning entry');
584
+ }
585
+ throw new KiokukoError('CONFLICT', 'Archive content already belongs to a different entry ID');
586
+ }
587
+ newEntries.push(entry);
588
+ }
589
+ for (const entry of entries) {
590
+ if (!duplicateIds.has(entry.id))
591
+ continue;
592
+ const existing = readEntry(database, { workspace, entryId: entry.id });
593
+ if (!entryMatchesArchive(existing, entry)) {
594
+ throw new KiokukoError('CONFLICT', `Entry ${entry.id} already exists with different archive metadata`);
595
+ }
596
+ }
597
+ return { newEntries, duplicates: duplicateIds.size };
598
+ }
599
+ function planRelatedImport(database, document, workspace) {
600
+ const links = [];
601
+ for (const link of document.links) {
602
+ const planned = {
603
+ from: stringValue(link.from_entry_id, 'link.from_entry_id'),
604
+ to: stringValue(link.to_entry_id, 'link.to_entry_id'),
605
+ relation: stringValue(link.relation, 'link.relation'),
606
+ createdAt: timestampValue(link.created_at, 'link.created_at'),
607
+ createdBy: stringValue(link.created_by, 'link.created_by'),
608
+ };
609
+ const existing = database?.prepare(`
610
+ SELECT created_at, created_by
611
+ FROM entry_links
612
+ WHERE from_entry_id = ? AND to_entry_id = ? AND relation = ?
613
+ `).get(planned.from, planned.to, planned.relation);
614
+ if (existing !== undefined) {
615
+ if (existing.created_at !== planned.createdAt || existing.created_by !== planned.createdBy) {
616
+ throw new KiokukoError('CONFLICT', 'Existing link has different archive metadata');
617
+ }
618
+ continue;
619
+ }
620
+ links.push(planned);
621
+ }
622
+ const audit = [];
623
+ for (const event of document.audit) {
624
+ const planned = {
625
+ eventId: stringValue(event.event_id, 'audit.event_id'),
626
+ entryId: nullableString(event.entry_id, 'audit.entry_id'),
627
+ operation: stringValue(event.operation, 'audit.operation'),
628
+ actor: stringValue(event.actor, 'audit.actor'),
629
+ detailsJson: stringValue(event.details_json, 'audit.details_json'),
630
+ createdAt: timestampValue(event.created_at, 'audit.created_at'),
631
+ };
632
+ const existing = database?.prepare(`
633
+ SELECT entry_id, workspace, operation, actor, details_json, created_at
634
+ FROM audit_events
635
+ WHERE event_id = ?
636
+ `).get(planned.eventId);
637
+ if (existing !== undefined) {
638
+ if (existing.entry_id !== planned.entryId
639
+ || existing.workspace !== workspace
640
+ || existing.operation !== planned.operation
641
+ || existing.actor !== planned.actor
642
+ || existing.details_json !== planned.detailsJson
643
+ || existing.created_at !== planned.createdAt) {
644
+ throw new KiokukoError('CONFLICT', `Audit event ${planned.eventId} already contains different metadata`);
645
+ }
646
+ continue;
647
+ }
648
+ audit.push(planned);
649
+ }
650
+ return { links, audit };
193
651
  }
194
- export async function importWorkspace(database, options) {
195
- const parsed = parseImport(await readFile(options.input, 'utf8'));
652
+ export async function importWorkspace(database, options, dependencies = {}) {
653
+ const archiveBytes = await readBoundedArchive(inputPathValue(options.input), dependencies);
654
+ validateWorkspaceArchiveByteLayout(archiveBytes);
655
+ if (archiveBytes[0] === 0xEF && archiveBytes[1] === 0xBB && archiveBytes[2] === 0xBF) {
656
+ throw new KiokukoError('VALIDATION_ERROR', 'Import must not begin with a UTF-8 BOM');
657
+ }
658
+ let archiveText;
659
+ try {
660
+ archiveText = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(archiveBytes);
661
+ }
662
+ catch {
663
+ throw new KiokukoError('VALIDATION_ERROR', 'Import is not valid UTF-8');
664
+ }
665
+ const parsed = parseImport(archiveText);
196
666
  const sourceWorkspace = requireWorkspace(parsed.manifest.workspace);
197
- const workspace = requireWorkspace(options.workspace ?? sourceWorkspace);
667
+ const workspace = requireWorkspace(options.workspace === undefined ? sourceWorkspace : stringValue(options.workspace, 'workspace'));
668
+ rejectSecretValue(workspace);
198
669
  const tagsByEntry = new Map();
199
670
  for (const tag of parsed.tags) {
200
671
  const entryId = stringValue(tag.entry_id, 'tag.entry_id');
@@ -206,40 +677,33 @@ export async function importWorkspace(database, options) {
206
677
  const sourceIds = new Set(entries.map((entry) => entry.id));
207
678
  if (sourceIds.size !== entries.length)
208
679
  throw new KiokukoError('VALIDATION_ERROR', 'Import contains duplicate entry IDs');
680
+ if (new Set(entries.map((entry) => entry.contentHash)).size !== entries.length) {
681
+ throw new KiokukoError('VALIDATION_ERROR', 'Import contains duplicate entry content');
682
+ }
683
+ validateSupersessionGraph(entries);
209
684
  validateRelatedLines(parsed, sourceIds);
210
685
  if (database === undefined && !options.dryRun)
211
686
  throw new KiokukoError('DATABASE_ERROR', 'Database is required for a non-dry import');
212
- const idMap = new Map();
213
- const newEntries = [];
214
- let duplicates = 0;
215
- for (const entry of entries) {
216
- if (!database) {
217
- idMap.set(entry.id, entry.id);
218
- newEntries.push(entry);
219
- continue;
220
- }
221
- const byId = existingEntry(database, entry.id);
222
- if (byId) {
223
- if (byId.content_hash !== entry.contentHash)
224
- throw new KiokukoError('CONFLICT', `Entry ID ${entry.id} already contains different content`);
225
- idMap.set(entry.id, entry.id);
226
- duplicates += 1;
227
- continue;
228
- }
229
- const byHash = database.prepare('SELECT entry_id AS id FROM entry_revisions WHERE workspace = ? AND content_hash = ? LIMIT 1').get(workspace, entry.contentHash);
230
- if (byHash) {
231
- idMap.set(entry.id, byHash.id);
232
- duplicates += 1;
233
- continue;
234
- }
235
- idMap.set(entry.id, entry.id);
236
- newEntries.push(entry);
237
- }
238
687
  if (options.dryRun) {
239
- return { count: entries.length, imported: newEntries.length, duplicates, dryRun: true, workspace };
688
+ const preflight = () => {
689
+ const entryPlan = planEntryImport(database, entries, workspace);
690
+ planRelatedImport(database, parsed, workspace);
691
+ return entryPlan;
692
+ };
693
+ const plan = database === undefined ? preflight() : withDeferredReadTransaction(database, preflight);
694
+ return {
695
+ count: entries.length,
696
+ imported: plan.newEntries.length,
697
+ duplicates: plan.duplicates,
698
+ dryRun: true,
699
+ workspace,
700
+ };
240
701
  }
241
702
  const db = database;
242
- withImmediateTransaction(db, () => {
703
+ const result = withImmediateTransaction(db, () => {
704
+ const plan = planEntryImport(db, entries, workspace);
705
+ const related = planRelatedImport(db, parsed, workspace);
706
+ const { newEntries } = plan;
243
707
  const insertEntry = db.prepare(`
244
708
  INSERT INTO entries (
245
709
  id, workspace, status, trust_level, confidence, current_revision,
@@ -247,7 +711,7 @@ export async function importWorkspace(database, options) {
247
711
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
248
712
  `);
249
713
  for (const entry of newEntries) {
250
- insertEntry.run(entry.id, workspace, entry.status, entry.trustLevel, entry.confidence, 1, entry.supersededBy === null ? null : idMap.get(entry.supersededBy) ?? entry.supersededBy, entry.createdBy, entry.createdAt, entry.updatedAt, entry.verifiedAt);
714
+ insertEntry.run(entry.id, workspace, 'candidate', entry.trustLevel, entry.confidence, 1, null, entry.createdBy, entry.createdAt, entry.updatedAt, null);
251
715
  insertEntryRevisionInTransaction(db, {
252
716
  entryId: entry.id,
253
717
  workspace,
@@ -256,8 +720,8 @@ export async function importWorkspace(database, options) {
256
720
  title: entry.title,
257
721
  body: entry.body,
258
722
  summary: entry.summary,
259
- scope: JSON.parse(entry.scopeJson),
260
- provenance: JSON.parse(entry.provenanceJson),
723
+ scope: entry.scope,
724
+ provenance: entry.provenance,
261
725
  tags: entry.tags,
262
726
  contentHash: entry.contentHash,
263
727
  createdBy: entry.createdBy,
@@ -269,24 +733,39 @@ export async function importWorkspace(database, options) {
269
733
  body: entry.body,
270
734
  summary: entry.summary,
271
735
  tags: entry.tags,
272
- scope: JSON.parse(entry.scopeJson),
736
+ scope: entry.scope,
273
737
  });
274
738
  }
739
+ const applyLifecycle = db.prepare(`
740
+ UPDATE entries
741
+ SET status = ?, superseded_by = ?, verified_at = ?
742
+ WHERE id = ? AND workspace = ? AND current_revision = 1
743
+ `);
744
+ for (const entry of newEntries) {
745
+ applyLifecycle.run(entry.status, entry.supersededBy, entry.verifiedAt, entry.id, workspace);
746
+ }
275
747
  const insertLink = db.prepare(`
276
- INSERT OR IGNORE INTO entry_links (from_entry_id, to_entry_id, relation, created_at, created_by)
748
+ INSERT INTO entry_links (from_entry_id, to_entry_id, relation, created_at, created_by)
277
749
  VALUES (?, ?, ?, ?, ?)
278
750
  `);
279
- for (const link of parsed.links) {
280
- insertLink.run(idMap.get(String(link.from_entry_id)) ?? String(link.from_entry_id), idMap.get(String(link.to_entry_id)) ?? String(link.to_entry_id), String(link.relation), String(link.created_at), String(link.created_by));
751
+ for (const link of related.links) {
752
+ insertLink.run(link.from, link.to, link.relation, link.createdAt, link.createdBy);
281
753
  }
282
754
  const insertAudit = db.prepare(`
283
- INSERT OR IGNORE INTO audit_events (event_id, entry_id, workspace, operation, actor, details_json, created_at)
755
+ INSERT INTO audit_events (event_id, entry_id, workspace, operation, actor, details_json, created_at)
284
756
  VALUES (?, ?, ?, ?, ?, ?, ?)
285
757
  `);
286
- for (const event of parsed.audit) {
287
- insertAudit.run(String(event.event_id), event.entry_id === null || event.entry_id === undefined ? null : idMap.get(String(event.entry_id)) ?? String(event.entry_id), workspace, String(event.operation), String(event.actor), String(event.details_json), String(event.created_at));
758
+ for (const event of related.audit) {
759
+ insertAudit.run(event.eventId, event.entryId, workspace, event.operation, event.actor, event.detailsJson, event.createdAt);
288
760
  }
761
+ return plan;
289
762
  });
290
- return { count: entries.length, imported: newEntries.length, duplicates, dryRun: false, workspace };
763
+ return {
764
+ count: entries.length,
765
+ imported: result.newEntries.length,
766
+ duplicates: result.duplicates,
767
+ dryRun: false,
768
+ workspace,
769
+ };
291
770
  }
292
771
  //# sourceMappingURL=import.js.map