@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
@@ -0,0 +1,921 @@
1
+ import { satisfiesFrameworkVersion } from '../repository/framework-version.js';
2
+ import { detectSkillGap } from './gap-detection.js';
3
+ import { buildSkillQueries } from './query-builder.js';
4
+ import { rankSkillCandidates } from './candidate-ranking.js';
5
+ import { documentsFromSkillSnapshot } from './import-preparation.js';
6
+ import { readSkillDiscoveryConfig } from './config.js';
7
+ import { SkillProviderError } from './providers/schema.js';
8
+ import { SkillDiscoveryCache } from './cache.js';
9
+ import { GitHubSkillSourceFetcher, SkillSourceError } from './source/github-fetcher.js';
10
+ import { validateSkillSnapshot } from './source/snapshot-validator.js';
11
+ import { clearPersistentSkillAuditFailure, clearPersistentSkillSourceFailure, externalSkillRefreshExpectation, externalSkillRequirement, externalSkillSourceFetchRequest, externalSkillWorkspace, markExternalSkillRefreshFailureInTransaction, persistExistingSkillImportInTransaction, persistNewSkillImportInTransaction, readExternalSkill, readPersistentSkillAuditFailure, readPersistentSkillSourceFailure, refreshExternalSkillSnapshotInTransaction, listExternalSkills, readPersistentSkillSearchCache, recordDiscoveredSkillInTransaction, writePersistentSkillAuditFailure, writePersistentSkillSearchCache, writePersistentSkillSourceFailure } from './store.js';
12
+ import { createSkillRegistryProvider, findSkills } from './find.js';
13
+ import { KiokukoError } from '../errors.js';
14
+ import { reviewedCatalogSkill, technologyDefinition } from './official-catalog.js';
15
+ import { isSqliteOperationalError } from '../db/sqlite-retry.js';
16
+ import { withImmediateTransaction } from '../db/transaction.js';
17
+ import { authorizeSkillMaterialization, hasSkillMaterializationAuthorization } from './materialization-authority.js';
18
+ import { DEFAULT_EXTERNAL_SKILL_FAILURE_TTL_MS, externalSkillFailureTtlMs } from './materialization-service.js';
19
+ import { compareCanonicalStrings } from '../serialization/validate.js';
20
+ function candidateId(candidate) { return `${candidate.sourceType}:${candidate.source}:${candidate.slug}`; }
21
+ function compatibilityProvider(provider) { return provider.startsWith('skills-sh-compat-'); }
22
+ function containsAlias(value, alias) {
23
+ const escaped = alias.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
24
+ return new RegExp(`(?:^|[^\\p{L}\\p{N}])${escaped}(?:$|[^\\p{L}\\p{N}])`, 'iu').test(value);
25
+ }
26
+ function providerFailureCode(error) {
27
+ if (!(error instanceof SkillProviderError))
28
+ return undefined;
29
+ const code = error.code;
30
+ switch (code) {
31
+ case 'registry_authentication_failed':
32
+ case 'registry_unavailable':
33
+ case 'registry_rate_limited':
34
+ return code;
35
+ case 'registry_invalid_response':
36
+ default:
37
+ throw error;
38
+ }
39
+ }
40
+ function sourceOrValidationFailureCode(error) {
41
+ if (!(error instanceof SkillSourceError))
42
+ return undefined;
43
+ const code = error.code;
44
+ switch (code) {
45
+ case 'source_missing':
46
+ return 'candidate_not_found_at_source';
47
+ case 'source_rate_limited':
48
+ case 'source_unavailable':
49
+ case 'candidate_not_found_at_source':
50
+ case 'source_tree_truncated':
51
+ case 'skill_disabled_for_model_invocation':
52
+ case 'skill_secret_detected':
53
+ case 'skill_too_large':
54
+ case 'skill_validation_failed':
55
+ case 'skill_blocked':
56
+ return code;
57
+ default:
58
+ throw error;
59
+ }
60
+ }
61
+ function persistenceFailureCode(error) {
62
+ if (error instanceof KiokukoError) {
63
+ if (error.code === 'CONFLICT')
64
+ return 'persistence_conflict';
65
+ return undefined;
66
+ }
67
+ return isSqliteOperationalError(error) ? 'persistence_failed' : undefined;
68
+ }
69
+ class CallerPersistenceAssertionError extends Error {
70
+ assertionError;
71
+ constructor(assertionError) {
72
+ super('Caller persistence assertion failed');
73
+ this.assertionError = assertionError;
74
+ this.name = 'CallerPersistenceAssertionError';
75
+ }
76
+ }
77
+ function cachedProviderFailure(value) {
78
+ if (value.failureCode === 'registry_authentication_failed')
79
+ return value.failureCode;
80
+ if (value.cacheOutcome === 'unavailable')
81
+ return 'registry_unavailable';
82
+ if (value.cacheOutcome === 'rate_limited')
83
+ return 'registry_rate_limited';
84
+ return undefined;
85
+ }
86
+ function requirementMatchScore(candidate, requirement, requirements) {
87
+ const values = [candidate.name, candidate.slug.split('/').at(-1) ?? candidate.slug].map((value) => value.toLowerCase());
88
+ const identities = [...new Set([requirement.id, requirement.technology].map((value) => value.toLowerCase()))];
89
+ if (values.some((value) => identities.includes(value)))
90
+ return 100;
91
+ if (values.some((value) => identities.some((identity) => containsAlias(value, identity))))
92
+ return 80;
93
+ const uniqueAliases = requirement.aliases.map((alias) => alias.toLowerCase()).filter((alias) => !requirements.some((other) => other !== requirement
94
+ && [other.id, other.technology, ...other.aliases].some((value) => value.toLowerCase() === alias)));
95
+ if (values.some((value) => uniqueAliases.some((alias) => containsAlias(value, alias))))
96
+ return 60;
97
+ return 0;
98
+ }
99
+ function requirementFor(candidate, requirements) {
100
+ return requirements.map((requirement) => ({ requirement, score: requirementMatchScore(candidate, requirement, requirements) }))
101
+ .filter(({ score }) => score > 0)
102
+ .sort((left, right) => right.score - left.score || compareCanonicalStrings(left.requirement.id, right.requirement.id))[0]?.requirement;
103
+ }
104
+ function normalizeOfficial(candidate, requirement) {
105
+ const official = reviewedCatalogSkill(candidate) !== undefined
106
+ ? 'catalog-verified'
107
+ : requirement.owners.some((owner) => candidate.source.toLowerCase().startsWith(`${owner.toLowerCase()}/`))
108
+ ? 'owner-verified'
109
+ : candidate.officialStatus;
110
+ return { ...candidate, officialStatus: official };
111
+ }
112
+ function requirementsForQuery(query, requirements) {
113
+ const normalized = query.normalize('NFKC').toLowerCase();
114
+ return requirements.filter((requirement) => {
115
+ const technology = requirement.technology.normalize('NFKC').toLowerCase();
116
+ return requirement.queries.some((value) => value.normalize('NFKC').toLowerCase() === normalized)
117
+ || normalized === `${technology} testing`
118
+ || normalized === `${technology} debug`
119
+ || normalized === `${technology} review`;
120
+ });
121
+ }
122
+ function locallySafeCommunityCandidate(candidate) {
123
+ return candidate.sourceType === 'github'
124
+ && !candidate.duplicate
125
+ && /^[A-Za-z0-9_.-]{1,100}\/[A-Za-z0-9_.-]{1,100}$/u.test(candidate.source)
126
+ && /^[A-Za-z0-9_.\-/]{1,240}$/u.test(candidate.slug)
127
+ && !candidate.slug.split('/').some((part) => part === '' || part === '.' || part === '..');
128
+ }
129
+ const CURATED_CACHE_QUERY = '__curated__';
130
+ const DEFAULT_AUTOMATIC_MATERIALIZATION_LIMIT = 1;
131
+ const MAX_AUTOMATIC_MATERIALIZATION_LIMIT = 2;
132
+ const MAX_SOURCE_ATTEMPTS = 6;
133
+ const MAX_COMMUNITY_AUDITS = 6;
134
+ const IMPORT_FRESHNESS_MS = 7 * 24 * 60 * 60_000;
135
+ function automaticMaterializationLimit(value) {
136
+ const resolved = value ?? DEFAULT_AUTOMATIC_MATERIALIZATION_LIMIT;
137
+ if (!Number.isSafeInteger(resolved) || resolved < 1 || resolved > MAX_AUTOMATIC_MATERIALIZATION_LIMIT) {
138
+ throw new KiokukoError('VALIDATION_ERROR', 'External skill materialization limit must be 1 or 2');
139
+ }
140
+ return resolved;
141
+ }
142
+ function providerNegativeCache(error) {
143
+ const code = error.code;
144
+ switch (code) {
145
+ case 'registry_rate_limited':
146
+ return { outcome: 'rate_limited', ttlMs: externalSkillFailureTtlMs(error.retryAfterSeconds) };
147
+ case 'registry_authentication_failed':
148
+ return { outcome: 'unavailable', ttlMs: DEFAULT_EXTERNAL_SKILL_FAILURE_TTL_MS, failureCode: code };
149
+ case 'registry_unavailable':
150
+ return { outcome: 'unavailable', ttlMs: DEFAULT_EXTERNAL_SKILL_FAILURE_TTL_MS };
151
+ case 'registry_invalid_response':
152
+ return undefined;
153
+ default:
154
+ throw error;
155
+ }
156
+ }
157
+ function frameworkVersionMatches(actual, expected) {
158
+ if (expected === undefined)
159
+ return true;
160
+ if (actual === undefined)
161
+ return false;
162
+ const compatibility = satisfiesFrameworkVersion(actual, expected);
163
+ return compatibility === 'exact' || compatibility === 'compatible';
164
+ }
165
+ function applicabilityMatchesFingerprint(requirement, fingerprint) {
166
+ const normalize = (value) => value.normalize('NFKC').toLowerCase();
167
+ const dimensionMatches = (expected, actual) => expected === undefined || expected.length === 0 || expected.some((value) => actual.some((candidate) => normalize(candidate) === normalize(value)));
168
+ if (!dimensionMatches(requirement.applicability.languages, fingerprint.languages)
169
+ || !dimensionMatches(requirement.applicability.databases, fingerprint.databases)
170
+ || !dimensionMatches(requirement.applicability.runtimes, fingerprint.runtimes)
171
+ || !dimensionMatches(requirement.applicability.tools, fingerprint.tools))
172
+ return false;
173
+ const frameworks = requirement.applicability.frameworks;
174
+ return frameworks === undefined || frameworks.length === 0 || frameworks.some((expected) => fingerprint.frameworks.some((actual) => normalize(actual.name) === normalize(expected.name) && frameworkVersionMatches(actual.version, expected.version)));
175
+ }
176
+ function officialStatus(value) {
177
+ return value === 'curated' || value === 'catalog-verified' || value === 'owner-verified' || value === 'registry-only' ? value : 'unknown';
178
+ }
179
+ function reusableImportedSkill(record, mode) {
180
+ if (reviewedCatalogSkill({ source: record.sourceLocator, slug: record.slug }) !== undefined)
181
+ return true;
182
+ // The materialized record itself crossed the runtime authority boundary.
183
+ // Its persisted audit label is descriptive and must not become a reusable
184
+ // authorization input for later discovery runs.
185
+ return mode === 'community';
186
+ }
187
+ function isReviewedCatalogCandidate(candidate, requirement) {
188
+ const definition = technologyDefinition(requirement.id);
189
+ return definition !== undefined
190
+ && requirement.repositories.some((source) => source.normalize('NFKC').toLowerCase() === candidate.source.normalize('NFKC').toLowerCase())
191
+ && (definition.reviewedSkills ?? []).some((skill) => skill.slug === candidate.slug);
192
+ }
193
+ function isReviewedCatalogIdentity(record, requirement) {
194
+ return isReviewedCatalogCandidate(candidateFromRecord(record), requirement);
195
+ }
196
+ function freshImportedSkill(database, requirement, rows, now, mode, fingerprint) {
197
+ const nowMs = Date.parse(now);
198
+ if (!Number.isFinite(nowMs))
199
+ return undefined;
200
+ return rows.find((record) => {
201
+ if (record.state !== 'imported' || record.sourceCommit === null || record.snapshotHash === null)
202
+ return false;
203
+ if (!reusableImportedSkill(record, mode))
204
+ return false;
205
+ const checkedAt = Date.parse(record.lastCheckedAt);
206
+ const age = nowMs - checkedAt;
207
+ if (!Number.isFinite(checkedAt) || age < 0 || age >= IMPORT_FRESHNESS_MS)
208
+ return false;
209
+ const importedRequirement = externalSkillRequirement(database, record.skillId);
210
+ if (importedRequirement === undefined)
211
+ return false;
212
+ return applicabilityMatchesFingerprint(importedRequirement, fingerprint)
213
+ && (requirementOverlaps(importedRequirement, requirement) || isReviewedCatalogIdentity(record, requirement));
214
+ });
215
+ }
216
+ function sourceSkillIdentity(value) {
217
+ return `${value.sourceType.normalize('NFKC').toLowerCase()}\u0000${value.source.normalize('NFKC').toLowerCase()}\u0000${value.slug}`;
218
+ }
219
+ function suppressAutomaticImport(record) {
220
+ return record !== undefined
221
+ && (record.disabledAt !== null || record.state === 'disabled' || record.state === 'blocked');
222
+ }
223
+ function sameSkillSource(record, candidate) {
224
+ return record.sourceType.normalize('NFKC').toLowerCase() === candidate.sourceType.normalize('NFKC').toLowerCase()
225
+ && record.sourceLocator.normalize('NFKC').toLowerCase() === candidate.source.normalize('NFKC').toLowerCase();
226
+ }
227
+ function requirementOverlaps(left, right) {
228
+ const normalize = (value) => value.normalize('NFKC').toLowerCase();
229
+ const leftCanonical = new Set([left.id, left.technology].map(normalize));
230
+ const rightCanonical = new Set([right.id, right.technology].map(normalize));
231
+ if ([...leftCanonical].some((value) => rightCanonical.has(value)))
232
+ return true;
233
+ const leftNames = new Set([left.id, left.technology, ...left.aliases].map(normalize));
234
+ const rightNames = new Set([right.id, right.technology, ...right.aliases].map(normalize));
235
+ return [...rightCanonical].some((value) => leftNames.has(value))
236
+ && [...leftCanonical].some((value) => rightNames.has(value));
237
+ }
238
+ function existingIdentityOwners(database, rows, candidate, canonical, requirement, fingerprint) {
239
+ const requestedIdentity = sourceSkillIdentity(candidate);
240
+ const canonicalIdentity = sourceSkillIdentity(canonical);
241
+ return rows.filter((record) => {
242
+ if (!sameSkillSource(record, canonical))
243
+ return false;
244
+ const identity = sourceSkillIdentity({ sourceType: record.sourceType, source: record.sourceLocator, slug: record.slug });
245
+ if (identity === requestedIdentity || identity === canonicalIdentity)
246
+ return true;
247
+ if (record.sourceCommit === null && record.snapshotHash === null && record.state === 'stale' && record.disabledAt === null)
248
+ return true;
249
+ const stored = externalSkillRequirement(database, record.skillId);
250
+ return stored !== undefined && requirementOverlaps(stored, requirement)
251
+ && (record.disabledAt !== null || record.state === 'disabled' || record.state === 'stale' || record.state === 'blocked'
252
+ || applicabilityMatchesFingerprint(stored, fingerprint));
253
+ });
254
+ }
255
+ function candidateFromRecord(record) {
256
+ if (record.sourceType !== 'github')
257
+ throw new KiokukoError('INTEGRITY_ERROR', 'External Skill source type is invalid');
258
+ return {
259
+ id: record.skillId,
260
+ provider: record.provider,
261
+ name: record.name,
262
+ slug: record.slug,
263
+ source: record.sourceLocator,
264
+ sourceType: record.sourceType,
265
+ installUrl: record.installUrl,
266
+ installs: record.installs,
267
+ duplicate: record.duplicate,
268
+ officialStatus: officialStatus(record.officialStatus),
269
+ auditStatus: record.auditStatus,
270
+ };
271
+ }
272
+ export class SkillDiscoveryService {
273
+ cache;
274
+ provider;
275
+ sourceFetcher;
276
+ sourceFlights = new Map();
277
+ now;
278
+ assertBeforePersist;
279
+ constructor(dependencies = {}) {
280
+ const config = readSkillDiscoveryConfig();
281
+ this.provider = dependencies.provider ?? createSkillRegistryProvider(dependencies.fetchImpl);
282
+ this.sourceFetcher = dependencies.sourceFetcher ?? new GitHubSkillSourceFetcher({ token: config.githubToken, ...(dependencies.fetchImpl ? { fetchImpl: dependencies.fetchImpl } : {}) });
283
+ this.cache = dependencies.cache ?? new SkillDiscoveryCache();
284
+ this.now = dependencies.now ?? (() => new Date().toISOString());
285
+ this.assertBeforePersist = dependencies.assertBeforePersist;
286
+ }
287
+ async fetchSnapshot(candidate, request) {
288
+ const key = JSON.stringify([candidateId(candidate), request.purpose, request.purpose === 'refresh' ? request.expectedPrimaryPath : null]);
289
+ const active = this.sourceFlights.get(key);
290
+ if (active)
291
+ return active;
292
+ const flight = this.sourceFetcher.fetch(candidate, request);
293
+ this.sourceFlights.set(key, flight);
294
+ try {
295
+ return await flight;
296
+ }
297
+ finally {
298
+ if (this.sourceFlights.get(key) === flight)
299
+ this.sourceFlights.delete(key);
300
+ }
301
+ }
302
+ async searchWithCache(database, input) {
303
+ const persistent = readPersistentSkillSearchCache(database, { ...input, provider: input.provider.id });
304
+ if (persistent !== null) {
305
+ if (persistent.failureCode === 'registry_authentication_failed') {
306
+ if (!input.allowAuthenticationFallback || input.provider.authenticationFallback === undefined)
307
+ return { result: persistent, cacheHits: 1, authenticationFallbackUsed: false, failureCode: persistent.failureCode };
308
+ const fallback = await this.searchWithCache(database, { ...input, provider: input.provider.authenticationFallback, allowAuthenticationFallback: false });
309
+ return { ...fallback, cacheHits: fallback.cacheHits + 1, authenticationFallbackUsed: true };
310
+ }
311
+ const failureCode = cachedProviderFailure(persistent);
312
+ if (failureCode !== undefined)
313
+ return { result: persistent, cacheHits: 1, authenticationFallbackUsed: false, failureCode };
314
+ return { result: persistent, cacheHits: 1, authenticationFallbackUsed: false };
315
+ }
316
+ try {
317
+ const loaded = await this.cache.getOrLoad({
318
+ provider: input.provider.id,
319
+ query: input.query,
320
+ mode: input.mode,
321
+ owner: input.owner,
322
+ loader: () => findSkills({ query: input.query, limit: 20, ...(input.owner === null ? {} : { owner: input.owner }) }, {
323
+ provider: input.provider,
324
+ fallbackOnAuthentication: false,
325
+ }),
326
+ });
327
+ writePersistentSkillSearchCache(database, {
328
+ provider: input.provider.id,
329
+ query: input.query,
330
+ owner: input.owner,
331
+ mode: input.mode,
332
+ result: loaded.result,
333
+ outcome: loaded.result.candidates.length === 0 ? 'empty' : 'success',
334
+ ttlMs: loaded.result.candidates.length === 0 ? 6 * 60 * 60_000 : 24 * 60 * 60_000,
335
+ now: input.now,
336
+ });
337
+ return { result: loaded.result, cacheHits: loaded.hit ? 1 : 0, authenticationFallbackUsed: false };
338
+ }
339
+ catch (error) {
340
+ if (error instanceof SkillProviderError) {
341
+ const negativeCache = providerNegativeCache(error);
342
+ if (negativeCache !== undefined) {
343
+ writePersistentSkillSearchCache(database, {
344
+ provider: input.provider.id,
345
+ query: input.query,
346
+ owner: input.owner,
347
+ mode: input.mode,
348
+ result: { provider: input.provider.id, experimental: compatibilityProvider(input.provider.id), candidates: [] },
349
+ outcome: negativeCache.outcome,
350
+ ...(negativeCache.failureCode === undefined ? {} : { failureCode: negativeCache.failureCode }),
351
+ ttlMs: negativeCache.ttlMs,
352
+ now: input.now,
353
+ });
354
+ if (error.code === 'registry_authentication_failed' && input.allowAuthenticationFallback && input.provider.authenticationFallback !== undefined) {
355
+ const fallback = await this.searchWithCache(database, { ...input, provider: input.provider.authenticationFallback, allowAuthenticationFallback: false });
356
+ return { ...fallback, authenticationFallbackUsed: true };
357
+ }
358
+ }
359
+ }
360
+ throw error;
361
+ }
362
+ }
363
+ async discover(database, input, modeOverride) {
364
+ const selectedSkillLimit = automaticMaterializationLimit(input.maxSelectedSkills);
365
+ const requestedMode = modeOverride ?? input.mode;
366
+ const now = this.now();
367
+ if (requestedMode === 'off')
368
+ return { attempted: false, mode: requestedMode, requirements: [], queries: [], cacheHits: 0, candidates: 0, selected: [], failures: [] };
369
+ const gap = detectSkillGap({ fingerprint: input.fingerprint, task: input.task, profile: input.profile, recommendedTags: input.recommendedTags, capabilities: input.capabilities, mode: requestedMode });
370
+ const mode = requestedMode === 'community' && gap.catalogAvailability === 'unknown' ? 'official' : requestedMode;
371
+ const base = { attempted: false, mode, requirements: [], queries: [], cacheHits: 0, candidates: 0, selected: [], failures: [] };
372
+ base.requirements = gap.missing.map((requirement) => requirement.id);
373
+ if (!gap.shouldDiscover || gap.missing.length === 0)
374
+ return base;
375
+ const existingSkills = listExternalSkills(database);
376
+ const unresolved = [];
377
+ const reused = new Set();
378
+ for (const requirement of gap.missing) {
379
+ const existing = freshImportedSkill(database, requirement, existingSkills, now, mode, input.fingerprint);
380
+ if (!existing) {
381
+ unresolved.push(requirement);
382
+ continue;
383
+ }
384
+ if (!reused.has(existing.skillId) && base.selected.length < selectedSkillLimit) {
385
+ reused.add(existing.skillId);
386
+ base.selected.push({ skillId: existing.skillId, name: existing.name, source: existing.sourceLocator, officialStatus: officialStatus(existing.officialStatus), imported: true, updated: false });
387
+ }
388
+ }
389
+ const nowMs = Date.parse(now);
390
+ if (!Number.isFinite(nowMs))
391
+ throw new KiokukoError('INTEGRITY_ERROR', 'Skill discovery clock is invalid');
392
+ const requirementsToSearch = base.selected.length >= selectedSkillLimit ? [] : unresolved;
393
+ if (requirementsToSearch.length === 0)
394
+ return base;
395
+ base.attempted = true;
396
+ const queries = buildSkillQueries({ requirements: requirementsToSearch, profile: input.profile, mode });
397
+ base.queries = queries;
398
+ for (const query of queries) {
399
+ if (requirementsForQuery(query, requirementsToSearch).length === 0)
400
+ throw new KiokukoError('INTEGRITY_ERROR', 'Generated Skill query has no requirement provenance');
401
+ }
402
+ const seenCandidates = new Set();
403
+ const officialCandidates = new Map();
404
+ const communityCandidates = new Map();
405
+ const associateCandidate = (target, candidate, requirements, forcedRequirement) => {
406
+ seenCandidates.add(candidateId(candidate));
407
+ const requirement = forcedRequirement ?? requirementFor(candidate, requirements);
408
+ if (requirement === undefined)
409
+ return;
410
+ const normalized = normalizeOfficial(candidate, requirement);
411
+ const identity = sourceSkillIdentity(normalized);
412
+ const current = target.get(identity);
413
+ const score = requirementMatchScore(normalized, requirement, gap.missing);
414
+ const currentScore = current === undefined ? -1 : requirementMatchScore(current.candidate, current.requirement, gap.missing);
415
+ if (current === undefined || score > currentScore || (score === currentScore && compareCanonicalStrings(requirement.id, current.requirement.id) < 0)) {
416
+ target.set(identity, { candidate: normalized, requirement });
417
+ }
418
+ };
419
+ let primaryAuthenticationFailed = false;
420
+ let providerRequestsLatched = false;
421
+ const search = async (query, owner, requirements, target) => {
422
+ if (providerRequestsLatched)
423
+ return;
424
+ try {
425
+ const loaded = await this.searchWithCache(database, { provider: this.provider, query, owner, mode, now, allowAuthenticationFallback: true });
426
+ base.cacheHits += loaded.cacheHits;
427
+ primaryAuthenticationFailed ||= loaded.authenticationFallbackUsed;
428
+ if (loaded.failureCode !== undefined) {
429
+ base.failures.push({ stage: 'search', code: loaded.failureCode });
430
+ providerRequestsLatched = true;
431
+ return;
432
+ }
433
+ for (const candidate of loaded.result.candidates)
434
+ associateCandidate(target, candidate, requirements);
435
+ }
436
+ catch (error) {
437
+ const code = providerFailureCode(error);
438
+ if (code === undefined)
439
+ throw error;
440
+ base.failures.push({ stage: 'search', code });
441
+ providerRequestsLatched = true;
442
+ }
443
+ };
444
+ // Phase 1: scoped searches against verified owners only.
445
+ for (const query of queries) {
446
+ const queryRequirements = requirementsForQuery(query, requirementsToSearch);
447
+ const owners = [...new Set(queryRequirements.flatMap((requirement) => requirement.owners))];
448
+ for (const owner of owners) {
449
+ await search(query, owner, queryRequirements.filter((requirement) => requirement.owners.includes(owner)), officialCandidates);
450
+ }
451
+ }
452
+ if (this.provider.curated && !primaryAuthenticationFailed && !providerRequestsLatched) {
453
+ const cachedCurated = readPersistentSkillSearchCache(database, { provider: this.provider.id, query: CURATED_CACHE_QUERY, owner: null, mode, now });
454
+ if (cachedCurated) {
455
+ base.cacheHits += 1;
456
+ const failureCode = cachedProviderFailure(cachedCurated);
457
+ if (failureCode !== undefined) {
458
+ base.failures.push({ stage: 'search', code: failureCode });
459
+ providerRequestsLatched = true;
460
+ }
461
+ else
462
+ for (const candidate of cachedCurated.candidates)
463
+ associateCandidate(officialCandidates, candidate, requirementsToSearch);
464
+ }
465
+ else {
466
+ try {
467
+ const curated = await this.provider.curated();
468
+ const result = { provider: this.provider.id, experimental: compatibilityProvider(this.provider.id), candidates: curated ?? [] };
469
+ writePersistentSkillSearchCache(database, { provider: this.provider.id, query: CURATED_CACHE_QUERY, owner: null, mode, result, outcome: result.candidates.length === 0 ? 'empty' : 'success', ttlMs: 6 * 60 * 60_000, now });
470
+ for (const candidate of result.candidates)
471
+ associateCandidate(officialCandidates, candidate, requirementsToSearch);
472
+ }
473
+ catch (error) {
474
+ const code = providerFailureCode(error);
475
+ if (code === undefined)
476
+ throw error;
477
+ if (error instanceof SkillProviderError) {
478
+ const negativeCache = providerNegativeCache(error);
479
+ if (negativeCache !== undefined) {
480
+ writePersistentSkillSearchCache(database, {
481
+ provider: this.provider.id,
482
+ query: CURATED_CACHE_QUERY,
483
+ owner: null,
484
+ mode,
485
+ result: { provider: this.provider.id, experimental: compatibilityProvider(this.provider.id), candidates: [] },
486
+ outcome: negativeCache.outcome,
487
+ ...(negativeCache.failureCode === undefined ? {} : { failureCode: negativeCache.failureCode }),
488
+ ttlMs: negativeCache.ttlMs,
489
+ now,
490
+ });
491
+ }
492
+ }
493
+ base.failures.push({ stage: 'search', code });
494
+ providerRequestsLatched = true;
495
+ }
496
+ }
497
+ }
498
+ const communityAudit = new Map();
499
+ let auditAttempts = 0;
500
+ let auditLimitReported = false;
501
+ let communityAuditLatched = false;
502
+ let communityAuditUnavailableReported = false;
503
+ const communityAllowed = async (candidate) => {
504
+ if (!locallySafeCommunityCandidate(candidate))
505
+ return null;
506
+ if (reviewedCatalogSkill(candidate) !== undefined)
507
+ return { candidate: { ...candidate, officialStatus: 'catalog-verified', auditStatus: 'not-required' } };
508
+ const key = candidateId(candidate);
509
+ const known = communityAudit.get(key);
510
+ if (known !== undefined)
511
+ return known.allowed
512
+ ? { candidate: { ...candidate, auditStatus: 'passed' }, authorization: known.authorization }
513
+ : null;
514
+ if (providerRequestsLatched || primaryAuthenticationFailed || communityAuditLatched || this.provider.audit === undefined) {
515
+ providerRequestsLatched = true;
516
+ communityAudit.set(key, { allowed: false });
517
+ communityAuditLatched = true;
518
+ if (!communityAuditUnavailableReported) {
519
+ base.failures.push({ stage: 'validation', code: 'community_audit_unavailable' });
520
+ communityAuditUnavailableReported = true;
521
+ }
522
+ return null;
523
+ }
524
+ const cachedAuditFailure = readPersistentSkillAuditFailure(database, this.provider.id, candidate, now);
525
+ if (cachedAuditFailure !== null) {
526
+ providerRequestsLatched = true;
527
+ communityAudit.set(key, { allowed: false });
528
+ communityAuditLatched = true;
529
+ if (!communityAuditUnavailableReported) {
530
+ base.failures.push({ stage: 'validation', code: 'community_audit_unavailable' });
531
+ communityAuditUnavailableReported = true;
532
+ }
533
+ return null;
534
+ }
535
+ if (auditAttempts >= MAX_COMMUNITY_AUDITS) {
536
+ communityAudit.set(key, { allowed: false });
537
+ if (!auditLimitReported) {
538
+ base.failures.push({ stage: 'validation', code: 'community_audit_limit_reached' });
539
+ auditLimitReported = true;
540
+ }
541
+ return null;
542
+ }
543
+ auditAttempts += 1;
544
+ let audit;
545
+ try {
546
+ audit = await authorizeSkillMaterialization(this.provider, candidate);
547
+ }
548
+ catch (error) {
549
+ const code = providerFailureCode(error);
550
+ if (code === undefined)
551
+ throw error;
552
+ if (error instanceof SkillProviderError && (error.code === 'registry_rate_limited' || error.code === 'registry_unavailable')) {
553
+ writePersistentSkillAuditFailure(database, this.provider.id, candidate, error.code, externalSkillFailureTtlMs(error.retryAfterSeconds), now);
554
+ }
555
+ providerRequestsLatched = true;
556
+ communityAudit.set(key, { allowed: false });
557
+ communityAuditLatched = true;
558
+ if (!communityAuditUnavailableReported) {
559
+ base.failures.push({ stage: 'validation', code: 'community_audit_unavailable' });
560
+ communityAuditUnavailableReported = true;
561
+ }
562
+ return null;
563
+ }
564
+ if (audit.status === 'unavailable') {
565
+ writePersistentSkillAuditFailure(database, this.provider.id, candidate, 'registry_unavailable', DEFAULT_EXTERNAL_SKILL_FAILURE_TTL_MS, now);
566
+ providerRequestsLatched = true;
567
+ communityAudit.set(key, { allowed: false });
568
+ communityAuditLatched = true;
569
+ if (!communityAuditUnavailableReported) {
570
+ base.failures.push({ stage: 'validation', code: 'community_audit_unavailable' });
571
+ communityAuditUnavailableReported = true;
572
+ }
573
+ return null;
574
+ }
575
+ clearPersistentSkillAuditFailure(database, this.provider.id, candidate);
576
+ const allowed = audit.status === 'passed' ? { candidate: audit.candidate, authorization: audit.authorization } : null;
577
+ communityAudit.set(key, allowed === null ? { allowed: false } : { allowed: true, authorization: allowed.authorization });
578
+ if (allowed === null)
579
+ base.failures.push({ stage: 'validation', code: 'community_audit_failed' });
580
+ return allowed;
581
+ };
582
+ const existingByIdentity = new Map(existingSkills.map((record) => [sourceSkillIdentity({ sourceType: record.sourceType, source: record.sourceLocator, slug: record.slug }), record]));
583
+ const successfulSkills = new Set(base.selected.flatMap((candidate) => {
584
+ const record = existingSkills.find((skill) => skill.skillId === candidate.skillId);
585
+ return record === undefined ? [] : [sourceSkillIdentity({ sourceType: record.sourceType, source: record.sourceLocator, slug: record.slug })];
586
+ }));
587
+ const successfulRequirements = new Set();
588
+ const attemptedSkills = new Set();
589
+ let sourceRequestsLatched = false;
590
+ const preparedSelections = [];
591
+ const pendingFailureTransitions = new Map();
592
+ const recordRecoverableFailure = (candidate, error, selectedExisting) => {
593
+ const code = sourceOrValidationFailureCode(error);
594
+ if (code === undefined)
595
+ return false;
596
+ const identity = sourceSkillIdentity(candidate);
597
+ const exactExisting = existingByIdentity.get(identity);
598
+ if (code === 'candidate_not_found_at_source') {
599
+ const selectedExact = selectedExisting !== undefined
600
+ && sourceSkillIdentity({ sourceType: selectedExisting.sourceType, source: selectedExisting.sourceLocator, slug: selectedExisting.slug }) === identity
601
+ ? selectedExisting
602
+ : undefined;
603
+ const existing = selectedExact ?? exactExisting;
604
+ if (existing === undefined)
605
+ pendingFailureTransitions.set(`new:${identity}`, { kind: 'new-stale', candidate });
606
+ else
607
+ pendingFailureTransitions.set(`existing:${existing.skillId}`, { kind: 'existing', record: existing, state: 'stale' });
608
+ }
609
+ else if ((code === 'skill_disabled_for_model_invocation' || code === 'skill_secret_detected' || code === 'skill_blocked') && exactExisting !== undefined) {
610
+ // A registry alias is only a hint. Never create or block that guessed
611
+ // identity when no verified canonical mapping exists.
612
+ pendingFailureTransitions.set(`existing:${exactExisting.skillId}`, { kind: 'existing', record: exactExisting, state: 'blocked' });
613
+ }
614
+ base.failures.push({ stage: /^(?:skill_|candidate_|source_tree)/u.test(code) ? 'validation' : 'source', code });
615
+ return true;
616
+ };
617
+ const processCandidates = async (candidates) => {
618
+ if (attemptedSkills.size >= MAX_SOURCE_ATTEMPTS || base.selected.length + preparedSelections.length >= selectedSkillLimit)
619
+ return;
620
+ const selected = new Map();
621
+ for (const requirement of requirementsToSearch) {
622
+ if (successfulRequirements.has(requirement.id))
623
+ continue;
624
+ const ranked = rankSkillCandidates({
625
+ candidates: [...candidates.values()].filter((item) => item.requirement.id === requirement.id).map((item) => item.candidate),
626
+ requirement,
627
+ task: input.task,
628
+ profile: input.profile,
629
+ mode,
630
+ });
631
+ for (const rankedCandidate of ranked) {
632
+ const candidate = rankedCandidate.candidate;
633
+ const identity = sourceSkillIdentity(candidate);
634
+ if (candidate.duplicate || selected.has(identity) || attemptedSkills.has(identity))
635
+ continue;
636
+ const owners = existingIdentityOwners(database, listExternalSkills(database), candidate, candidate, requirement, input.fingerprint);
637
+ const exactOwners = owners.filter((record) => sourceSkillIdentity({ sourceType: record.sourceType, source: record.sourceLocator, slug: record.slug }) === identity);
638
+ if (exactOwners.length > 1 || (exactOwners.length === 0 && owners.length > 1))
639
+ throw new KiokukoError('CONFLICT', 'External Skill canonical identity is ambiguous');
640
+ const existing = exactOwners[0] ?? owners[0];
641
+ if (suppressAutomaticImport(existing))
642
+ continue;
643
+ const audited = await communityAllowed(candidate);
644
+ if (audited === null)
645
+ continue;
646
+ selected.set(identity, { candidate: audited.candidate, requirement, ...(existing === undefined ? {} : { existing }) });
647
+ attemptedSkills.add(identity);
648
+ if (attemptedSkills.size >= MAX_SOURCE_ATTEMPTS)
649
+ break;
650
+ }
651
+ if (attemptedSkills.size >= MAX_SOURCE_ATTEMPTS)
652
+ break;
653
+ }
654
+ for (const { candidate, requirement, existing: selectedExisting } of selected.values()) {
655
+ if (sourceRequestsLatched)
656
+ break;
657
+ const identity = sourceSkillIdentity(candidate);
658
+ if (successfulRequirements.has(requirement.id) || successfulSkills.has(identity))
659
+ continue;
660
+ const cachedSourceFailure = readPersistentSkillSourceFailure(database, candidate, now);
661
+ if (cachedSourceFailure !== null) {
662
+ base.failures.push({ stage: 'source', code: cachedSourceFailure.code });
663
+ continue;
664
+ }
665
+ let snapshot;
666
+ let documents;
667
+ let authorizedCanonicalCandidate;
668
+ let authorization;
669
+ try {
670
+ let fetchRequest = { purpose: 'discovery' };
671
+ if (selectedExisting !== undefined) {
672
+ const detail = readExternalSkill(database, selectedExisting.skillId);
673
+ if (detail === undefined)
674
+ throw new KiokukoError('CONFLICT', 'External Skill changed during discovery');
675
+ fetchRequest = externalSkillSourceFetchRequest(detail);
676
+ }
677
+ const fetched = await this.fetchSnapshot(candidate, fetchRequest);
678
+ snapshot = validateSkillSnapshot({
679
+ candidate,
680
+ sourceCommit: fetched.sourceCommit,
681
+ files: fetched.files.map((file) => ({ path: file.path, content: file.content, primary: file.primary })),
682
+ });
683
+ const reviewedCatalog = isReviewedCatalogCandidate(candidate, requirement) ? reviewedCatalogSkill(candidate) : undefined;
684
+ const primaryPath = snapshot.files.find((file) => file.primary)?.path;
685
+ if (reviewedCatalog !== undefined && (primaryPath === undefined || !reviewedCatalog.primaryPaths.includes(primaryPath))) {
686
+ throw new SkillSourceError('skill_validation_failed');
687
+ }
688
+ if (requirementMatchScore(snapshot.candidate, requirement, gap.missing) === 0)
689
+ throw new SkillSourceError('skill_validation_failed');
690
+ snapshot = { ...snapshot, candidate: normalizeOfficial(snapshot.candidate, requirement) };
691
+ const authorized = await communityAllowed(snapshot.candidate);
692
+ if (authorized === null)
693
+ continue;
694
+ authorizedCanonicalCandidate = authorized.candidate;
695
+ authorization = authorized.authorization;
696
+ documents = documentsFromSkillSnapshot(snapshot);
697
+ clearPersistentSkillSourceFailure(database, candidate);
698
+ }
699
+ catch (error) {
700
+ if (error instanceof SkillSourceError && (error.code === 'source_rate_limited' || error.code === 'source_unavailable')) {
701
+ writePersistentSkillSourceFailure(database, candidate, error.code, externalSkillFailureTtlMs(error.retryAfterSeconds), now);
702
+ if (error.code === 'source_rate_limited')
703
+ sourceRequestsLatched = true;
704
+ }
705
+ if (!recordRecoverableFailure(candidate, error, selectedExisting))
706
+ throw error;
707
+ continue;
708
+ }
709
+ const canonicalCandidate = authorizedCanonicalCandidate;
710
+ const canonicalIdentity = sourceSkillIdentity(canonicalCandidate);
711
+ if (successfulSkills.has(canonicalIdentity))
712
+ continue;
713
+ preparedSelections.push({
714
+ requestedCandidate: candidate,
715
+ canonicalCandidate,
716
+ ...(authorization === undefined ? {} : { authorization }),
717
+ requirement,
718
+ ...(selectedExisting === undefined ? {} : { selectedExisting }),
719
+ ...(existingByIdentity.get(canonicalIdentity) === undefined ? {} : { observedCanonical: existingByIdentity.get(canonicalIdentity) }),
720
+ snapshot: { ...snapshot, candidate: canonicalCandidate },
721
+ documents,
722
+ });
723
+ successfulSkills.add(canonicalIdentity);
724
+ successfulRequirements.add(requirement.id);
725
+ if (base.selected.length + preparedSelections.length >= selectedSkillLimit)
726
+ break;
727
+ }
728
+ };
729
+ // Owner-scoped and curated candidates are source-validated before any
730
+ // unscoped community query leaves the process.
731
+ await processCandidates(officialCandidates);
732
+ // Phase 3: general registry search is community-only and runs only for
733
+ // requirements that direct/owner-verified discovery did not satisfy.
734
+ if (mode === 'community' && base.selected.length + preparedSelections.length < selectedSkillLimit && attemptedSkills.size < MAX_SOURCE_ATTEMPTS) {
735
+ const remaining = requirementsToSearch.filter((requirement) => !successfulRequirements.has(requirement.id));
736
+ for (const query of queries) {
737
+ const queryRequirements = requirementsForQuery(query, remaining);
738
+ if (queryRequirements.length > 0)
739
+ await search(query, null, queryRequirements, communityCandidates);
740
+ }
741
+ await processCandidates(communityCandidates);
742
+ }
743
+ if (preparedSelections.length > 0 || pendingFailureTransitions.size > 0) {
744
+ let outcomes;
745
+ try {
746
+ outcomes = withImmediateTransaction(database, () => {
747
+ // Every current-state and CAS check runs under the same write lock,
748
+ // before the first candidate import mutates durable state.
749
+ try {
750
+ this.assertBeforePersist?.();
751
+ }
752
+ catch (error) {
753
+ // Caller-owned state assertions are not persistence failures. Keep
754
+ // them out of the recoverable CAS/SQLite summary boundary below.
755
+ throw new CallerPersistenceAssertionError(error);
756
+ }
757
+ const currentSkills = listExternalSkills(database);
758
+ const failureTransitions = [];
759
+ for (const transition of pendingFailureTransitions.values()) {
760
+ if (transition.kind === 'new-stale') {
761
+ const identity = sourceSkillIdentity(transition.candidate);
762
+ if (!currentSkills.some((record) => sourceSkillIdentity({ sourceType: record.sourceType, source: record.sourceLocator, slug: record.slug }) === identity)) {
763
+ failureTransitions.push(transition);
764
+ }
765
+ continue;
766
+ }
767
+ const current = currentSkills.find((record) => record.skillId === transition.record.skillId);
768
+ const expected = externalSkillRefreshExpectation(transition.record);
769
+ if (current === undefined
770
+ || current.generation !== expected.generation
771
+ || current.sourceCommit !== expected.sourceCommit
772
+ || current.snapshotHash !== expected.snapshotHash
773
+ || current.state !== expected.state
774
+ || current.lastCheckedAt !== expected.lastCheckedAt) {
775
+ throw new KiokukoError('CONFLICT', 'External Skill changed during discovery');
776
+ }
777
+ failureTransitions.push(transition);
778
+ }
779
+ const resolvedSelections = preparedSelections.map((plan) => {
780
+ const { requestedCandidate, canonicalCandidate, requirement, selectedExisting, observedCanonical, snapshot, documents } = plan;
781
+ const canonicalIdentity = sourceSkillIdentity(canonicalCandidate);
782
+ const selectedMaterialized = selectedExisting !== undefined && (selectedExisting.sourceCommit !== null || selectedExisting.snapshotHash !== null);
783
+ const canonicalTarget = selectedExisting !== undefined && !selectedMaterialized && observedCanonical !== undefined && selectedExisting.skillId !== observedCanonical.skillId
784
+ ? { skillId: observedCanonical.skillId, expected: externalSkillRefreshExpectation(observedCanonical) }
785
+ : undefined;
786
+ const observedExisting = selectedExisting ?? observedCanonical;
787
+ const currentOwners = existingIdentityOwners(database, currentSkills, requestedCandidate, canonicalCandidate, requirement, input.fingerprint);
788
+ const concurrentlyImported = observedExisting === undefined && currentOwners.length === 1
789
+ ? currentOwners.find((record) => {
790
+ if (sourceSkillIdentity({ sourceType: record.sourceType, source: record.sourceLocator, slug: record.slug }) !== canonicalIdentity
791
+ || record.state !== 'imported' || record.sourceCommit !== snapshot.sourceCommit || record.snapshotHash !== snapshot.snapshotHash
792
+ || !hasSkillMaterializationAuthorization(canonicalCandidate, plan.authorization))
793
+ return false;
794
+ const storedRequirement = externalSkillRequirement(database, record.skillId);
795
+ return storedRequirement !== undefined && requirementOverlaps(storedRequirement, requirement)
796
+ && applicabilityMatchesFingerprint(storedRequirement, input.fingerprint);
797
+ })
798
+ : undefined;
799
+ if (concurrentlyImported !== undefined)
800
+ return { kind: 'reused', plan, record: concurrentlyImported };
801
+ const allowedOwnerIds = new Set([selectedExisting?.skillId, observedCanonical?.skillId].filter((value) => value !== undefined));
802
+ if (currentOwners.some((record) => !allowedOwnerIds.has(record.skillId))
803
+ || observedExisting === undefined && currentOwners.length > 0
804
+ || selectedExisting === undefined && currentOwners.length > 1
805
+ || selectedExisting !== undefined && !selectedMaterialized && currentOwners.length > (canonicalTarget === undefined ? 1 : 2)) {
806
+ throw new KiokukoError('CONFLICT', 'External Skill changed during discovery');
807
+ }
808
+ const currentObserved = observedExisting === undefined ? undefined : currentSkills.find((record) => record.skillId === observedExisting.skillId);
809
+ if (observedExisting !== undefined && currentObserved === undefined)
810
+ throw new KiokukoError('CONFLICT', 'External Skill changed during discovery');
811
+ if (suppressAutomaticImport(currentObserved))
812
+ return { kind: 'skipped', plan };
813
+ const prepared = {
814
+ skill: canonicalCandidate,
815
+ sourceWorkspace: externalSkillWorkspace(canonicalCandidate),
816
+ sourceCommit: snapshot.sourceCommit,
817
+ snapshotHash: snapshot.snapshotHash,
818
+ frontmatter: snapshot.frontmatter,
819
+ documents,
820
+ requirement,
821
+ };
822
+ if (observedExisting === undefined)
823
+ return { kind: 'new', plan, input: prepared };
824
+ if (observedExisting.sourceCommit === null && observedExisting.snapshotHash === null) {
825
+ return { kind: 'existing', plan, input: prepared, existing: observedExisting, ...(canonicalTarget === undefined ? {} : { canonicalTarget }) };
826
+ }
827
+ return { kind: 'refresh', plan, input: prepared, existing: observedExisting };
828
+ });
829
+ for (const transition of failureTransitions) {
830
+ if (transition.kind === 'existing') {
831
+ markExternalSkillRefreshFailureInTransaction(database, transition.record.skillId, transition.state, externalSkillRefreshExpectation(transition.record), now);
832
+ }
833
+ else {
834
+ const discovered = recordDiscoveredSkillInTransaction(database, transition.candidate, now);
835
+ markExternalSkillRefreshFailureInTransaction(database, discovered.skillId, 'stale', externalSkillRefreshExpectation(discovered), now);
836
+ }
837
+ }
838
+ return resolvedSelections.map((selection) => {
839
+ if (selection.kind === 'reused' || selection.kind === 'skipped')
840
+ return selection;
841
+ if (selection.kind === 'new') {
842
+ return { kind: 'selected', plan: selection.plan, result: persistNewSkillImportInTransaction(database, selection.input, now, selection.plan.authorization) };
843
+ }
844
+ if (selection.kind === 'existing') {
845
+ return {
846
+ kind: 'selected',
847
+ plan: selection.plan,
848
+ result: persistExistingSkillImportInTransaction(database, selection.existing.skillId, selection.input, externalSkillRefreshExpectation(selection.existing), now, selection.canonicalTarget, selection.plan.authorization),
849
+ };
850
+ }
851
+ const result = refreshExternalSkillSnapshotInTransaction(database, selection.existing.skillId, selection.plan.snapshot, selection.input.documents, selection.plan.requirement, externalSkillRefreshExpectation(selection.existing), now, selection.plan.authorization);
852
+ return result.kind === 'staled'
853
+ ? { kind: 'staled', plan: selection.plan }
854
+ : { kind: 'selected', plan: selection.plan, result: result.result };
855
+ });
856
+ });
857
+ }
858
+ catch (error) {
859
+ if (error instanceof CallerPersistenceAssertionError)
860
+ throw error.assertionError;
861
+ const code = persistenceFailureCode(error);
862
+ if (code === undefined)
863
+ throw error;
864
+ base.failures.push({ stage: 'persistence', code });
865
+ outcomes = [];
866
+ }
867
+ for (const outcome of outcomes) {
868
+ if (outcome.kind === 'staled') {
869
+ base.failures.push({ stage: 'persistence', code: 'persistence_conflict' });
870
+ continue;
871
+ }
872
+ if (outcome.kind === 'skipped' || base.selected.length >= selectedSkillLimit)
873
+ continue;
874
+ if (outcome.kind === 'reused') {
875
+ if (!base.selected.some((item) => item.skillId === outcome.record.skillId)) {
876
+ base.selected.push({ skillId: outcome.record.skillId, name: outcome.record.name, source: outcome.record.sourceLocator, officialStatus: officialStatus(outcome.record.officialStatus), imported: true, updated: false });
877
+ }
878
+ continue;
879
+ }
880
+ if (!base.selected.some((item) => item.skillId === outcome.result.skillId)) {
881
+ base.selected.push({
882
+ skillId: outcome.result.skillId,
883
+ name: outcome.plan.canonicalCandidate.name,
884
+ source: outcome.plan.canonicalCandidate.source,
885
+ officialStatus: outcome.plan.canonicalCandidate.officialStatus,
886
+ imported: outcome.result.imported > 0,
887
+ updated: outcome.result.updated,
888
+ });
889
+ }
890
+ }
891
+ }
892
+ base.candidates = seenCandidates.size;
893
+ return base;
894
+ }
895
+ }
896
+ export async function discoverSkills(database, input, dependencies = {}) {
897
+ const effectiveDependencies = dependencies.fetchImpl === undefined && input.fetchImpl !== undefined
898
+ ? { ...dependencies, fetchImpl: input.fetchImpl }
899
+ : dependencies;
900
+ return discoveryServiceFor(effectiveDependencies).discover(database, input);
901
+ }
902
+ let sharedDiscoveryService;
903
+ const fetchDiscoveryServices = new WeakMap();
904
+ function discoveryServiceFor(dependencies) {
905
+ const keys = Object.keys(dependencies);
906
+ if (keys.length === 0) {
907
+ sharedDiscoveryService ??= new SkillDiscoveryService();
908
+ return sharedDiscoveryService;
909
+ }
910
+ if (keys.length === 1 && dependencies.fetchImpl !== undefined) {
911
+ const key = dependencies.fetchImpl;
912
+ const existing = fetchDiscoveryServices.get(key);
913
+ if (existing)
914
+ return existing;
915
+ const service = new SkillDiscoveryService(dependencies);
916
+ fetchDiscoveryServices.set(key, service);
917
+ return service;
918
+ }
919
+ return new SkillDiscoveryService(dependencies);
920
+ }
921
+ //# sourceMappingURL=discovery-service.js.map