@xopcai/xopc 0.0.159 → 0.0.161

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 (215) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{action-boundary-settings-panel-CaRK2vIy.js → action-boundary-settings-panel-BIVBUiNT.js} +1 -1
  4. package/dist/gateway/static/root/assets/activity-store-o7GRljfD.js +1 -0
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BeVf03hx.js → agent-avatar-dicebear-9R3szLMc.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-_qbUaYbs.js +2 -0
  7. package/dist/gateway/static/root/assets/{api-BjcBIV2O.js → api-jSTqFMq9.js} +1 -1
  8. package/dist/gateway/static/root/assets/{app-management-settings-panel-BG2ulMYw.js → app-management-settings-panel-D81cyE76.js} +1 -1
  9. package/dist/gateway/static/root/assets/{appearance-settings-0Kjenr2T.js → appearance-settings-Ct6bZQCV.js} +1 -1
  10. package/dist/gateway/static/root/assets/apps-page-0sPqD0FK.js +1 -0
  11. package/dist/gateway/static/root/assets/{archive-plugin-ChiNnH1e.js → archive-plugin-CfD7AZKn.js} +1 -1
  12. package/dist/gateway/static/root/assets/{attachment-load-pbBlQR-g.js → attachment-load-CP_Gaxt3.js} +1 -1
  13. package/dist/gateway/static/root/assets/{automation-suggestion-card-CTZDOyDy.js → automation-suggestion-card-BGKM_NDZ.js} +1 -1
  14. package/dist/gateway/static/root/assets/automations-page-BxWBuBsE.js +6 -0
  15. package/dist/gateway/static/root/assets/{binary-plugins-BeZGzpGF.js → binary-plugins-IPr2EVnS.js} +1 -1
  16. package/dist/gateway/static/root/assets/{block-editor-BnDmhN5Z.js → block-editor-B6aHEGA6.js} +3 -3
  17. package/dist/gateway/static/root/assets/browser-settings-page-C499b9AH.js +8 -0
  18. package/dist/gateway/static/root/assets/{capability-presets-api-Ii36j_7v.js → capability-presets-api-CCX9-LjU.js} +1 -1
  19. package/dist/gateway/static/root/assets/{capability-presets-settings-panel-Cl3dw4yh.js → capability-presets-settings-panel-Dva9SiPK.js} +2 -2
  20. package/dist/gateway/static/root/assets/{channel-recipient-api-CFqrSi5K.js → channel-recipient-api-MmLMLDWf.js} +1 -1
  21. package/dist/gateway/static/root/assets/channels-settings-DNAU7u9o.js +1 -0
  22. package/dist/gateway/static/root/assets/channels-status-swr-CIOH7tgn.js +1 -0
  23. package/dist/gateway/static/root/assets/check-DUBLcKHA.js +1 -0
  24. package/dist/gateway/static/root/assets/circle-alert-BEjgurr2.js +1 -0
  25. package/dist/gateway/static/root/assets/cn-JdwAhgNB.js +1 -0
  26. package/dist/gateway/static/root/assets/{connector-logo-CiLxuZ8S.js → connector-logo-BbjzIs87.js} +1 -1
  27. package/dist/gateway/static/root/assets/connectors-page-TwiBp8vY.js +2 -0
  28. package/dist/gateway/static/root/assets/createLucideIcon-DPHK1VkS.js +1 -0
  29. package/dist/gateway/static/root/assets/desktop-pet-CYH5ZSXy.js +1 -0
  30. package/dist/gateway/static/root/assets/desktop-pet-settings-DQLqGLc0.js +1 -0
  31. package/dist/gateway/static/root/assets/desktop-pet-sprite-DTqXSpqf.js +1 -0
  32. package/dist/gateway/static/root/assets/{directory-picker-path-field-CC2VL5Mc.js → directory-picker-path-field-ClsiXhmz.js} +1 -1
  33. package/dist/gateway/static/root/assets/{dreaming-settings-KY7ZfVsy.js → dreaming-settings-CjMPPdzg.js} +3 -3
  34. package/dist/gateway/static/root/assets/{extension-debug-page-C8KBeucs.js → extension-debug-page-57eizjhB.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-page-CV-5Kcyk.js → extension-page-CsbSb3u1.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-settings-page-DvUtDrAu.js → extension-settings-page-DKA9-lyP.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-config-api-DXfVeJLc.js → gateway-config-api-DIPJuzBZ.js} +1 -1
  38. package/dist/gateway/static/root/assets/gateway-settings-CDLqAQY7.js +1 -0
  39. package/dist/gateway/static/root/assets/{gateway-startup-retry-cxQ94qMm.js → gateway-startup-retry-DrfSJtvT.js} +1 -1
  40. package/dist/gateway/static/root/assets/goal-create-dialog-Q3B0E-0s.js +1 -0
  41. package/dist/gateway/static/root/assets/goal-detail-page-C5VAK04G.js +4 -0
  42. package/dist/gateway/static/root/assets/{goals-config-api-P8VhLqYF.js → goals-config-api-lGC10UkU.js} +1 -1
  43. package/dist/gateway/static/root/assets/goals-page-BJb-5IYw.js +2 -0
  44. package/dist/gateway/static/root/assets/{goals-settings-DHXyaH_O.js → goals-settings-BWq0pdrA.js} +1 -1
  45. package/dist/gateway/static/root/assets/{heartbeat-settings-CNckP_Dv.js → heartbeat-settings-D4o8BJVv.js} +1 -1
  46. package/dist/gateway/static/root/assets/index-CkUHH170.css +1 -0
  47. package/dist/gateway/static/root/assets/index-vq_cJZJC.js +199 -0
  48. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-jXmv1RVX.js → keyboard-shortcuts-settings-6T2QYehS.js} +1 -1
  49. package/dist/gateway/static/root/assets/local-app-workbench-page-euH17Kvb.js +2 -0
  50. package/dist/gateway/static/root/assets/{local-apps-page-Cs5LKjtc.js → local-apps-page-DCjg2K9f.js} +1 -1
  51. package/dist/gateway/static/root/assets/{locale-store-C_M71MK9.js → locale-store-Dar-xqQr.js} +1 -1
  52. package/dist/gateway/static/root/assets/logs-page-CyYCBqym.js +7 -0
  53. package/dist/gateway/static/root/assets/{markdown-split-BFqeSNte.js → markdown-split-rt1XO43g.js} +1 -1
  54. package/dist/gateway/static/root/assets/{media-plugins-Cc8379gq.js → media-plugins-Bwo8CJuh.js} +1 -1
  55. package/dist/gateway/static/root/assets/messages-CxySDn9d.js +5 -0
  56. package/dist/gateway/static/root/assets/models-hub-panel-DObxxjc9.js +2 -0
  57. package/dist/gateway/static/root/assets/{notes-page-DlGU4ipo.js → notes-page-Dp-pkX7d.js} +1 -1
  58. package/dist/gateway/static/root/assets/notes-workbench-CCb4RXcZ.js +4 -0
  59. package/dist/gateway/static/root/assets/{page-tabs-kFOswGlG.js → page-tabs-CzZ4iKOb.js} +1 -1
  60. package/dist/gateway/static/root/assets/{preview-open-alternatives-CvjuvABg.js → preview-open-alternatives-3E_Qa-58.js} +1 -1
  61. package/dist/gateway/static/root/assets/product-delivery-BTRT9kCS.js +1 -0
  62. package/dist/gateway/static/root/assets/product-open-page-B2T48Sbt.js +1 -0
  63. package/dist/gateway/static/root/assets/project-detail-page-BvEcriRC.js +1 -0
  64. package/dist/gateway/static/root/assets/projects-page-B_e79MRJ.js +1 -0
  65. package/dist/gateway/static/root/assets/remote-access-hub-DGcqnYfR.js +1 -0
  66. package/dist/gateway/static/root/assets/{save-bar-controls-DwrD7ODx.js → save-bar-controls-CkeKasGm.js} +1 -1
  67. package/dist/gateway/static/root/assets/{schedule-field-Ba4WzTmE.js → schedule-field-TkqUL3wm.js} +1 -1
  68. package/dist/gateway/static/root/assets/{schema-form-DvydL8eF.js → schema-form-DC-iU5IG.js} +1 -1
  69. package/dist/gateway/static/root/assets/sessions-page-DQZhrDJ8.js +1 -0
  70. package/dist/gateway/static/root/assets/settings-advanced-gate-BkdeN99Y.js +1 -0
  71. package/dist/gateway/static/root/assets/{settings-collapsible-section-LaQqGGej.js → settings-collapsible-section-Dpqth3oA.js} +1 -1
  72. package/dist/gateway/static/root/assets/{settings-form-section-DruI__qO.js → settings-form-section-CNqF6IwY.js} +1 -1
  73. package/dist/gateway/static/root/assets/{settings-loading-skeleton-UvA-eDhF.js → settings-loading-skeleton-DkvwitK_.js} +1 -1
  74. package/dist/gateway/static/root/assets/{settings-page-layout-DG-XMIPu.js → settings-page-layout-DWvNgISx.js} +1 -1
  75. package/dist/gateway/static/root/assets/{settings-page-5oCGSebm.js → settings-page-qPG7I2zd.js} +1 -1
  76. package/dist/gateway/static/root/assets/setup-status-panel-lwRAh9aw.js +1 -0
  77. package/dist/gateway/static/root/assets/{share-preview-page-FLVeTrRt.js → share-preview-page-DGpSPW-n.js} +1 -1
  78. package/dist/gateway/static/root/assets/shares-settings-BGj9IVUr.js +1 -0
  79. package/dist/gateway/static/root/assets/skill-reload-api-B2UBAs5U.js +1 -0
  80. package/dist/gateway/static/root/assets/skills-page-DdjzFZ8I.js +2 -0
  81. package/dist/gateway/static/root/assets/{system-settings-panel-CeK93xVi.js → system-settings-panel-8TxxSbUw.js} +1 -1
  82. package/dist/gateway/static/root/assets/{theme-store-C8ohIHhw.js → theme-store-B5FzMWOx.js} +1 -1
  83. package/dist/gateway/static/root/assets/{typed-models-lib-D1Lr2gG0.js → typed-models-lib-DEGtSA5h.js} +1 -1
  84. package/dist/gateway/static/root/assets/{use-channel-catalog-BWzou5ar.js → use-channel-catalog-BDU6FneG.js} +1 -1
  85. package/dist/gateway/static/root/assets/user-context-page-BTTZpAGV.js +1 -0
  86. package/dist/gateway/static/root/assets/voice-api-key-field-DuDQd-WU.js +1 -0
  87. package/dist/gateway/static/root/assets/work-discovery-page-BUVkVCsW.js +1 -0
  88. package/dist/gateway/static/root/assets/work-item-detail-page-DeEPPu4n.js +1 -0
  89. package/dist/gateway/static/root/assets/work-page-DQNUyTUq.js +1 -0
  90. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Bynqe4kV.js → workflow-run-setup-panel-Cwi8Pz7n.js} +2 -2
  91. package/dist/gateway/static/root/assets/workflows-page-BIy4wrzO.js +11 -0
  92. package/dist/gateway/static/root/index.html +12 -10
  93. package/dist/package.js +1 -1
  94. package/dist/src/automations/service/action-executor.js +1 -1
  95. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  96. package/dist/src/config/agent-profile.js +1 -1
  97. package/dist/src/gateway/agents-admin.js +1 -1
  98. package/dist/src/gateway/chat-stream/mapper.d.ts +1 -0
  99. package/dist/src/gateway/chat-stream/mapper.js +43 -7
  100. package/dist/src/gateway/hono/routes/home.js +15 -2
  101. package/dist/src/gateway/hono/routes/work-discovery.js +451 -0
  102. package/dist/src/gateway/hono/sse.d.ts +2 -0
  103. package/dist/src/gateway/hono/sse.js +10 -3
  104. package/dist/src/gateway/run-loop.js +17 -7
  105. package/dist/src/gateway/server.d.ts +1 -0
  106. package/dist/src/gateway/server.js +9 -0
  107. package/dist/src/gateway/service.d.ts +2 -0
  108. package/dist/src/gateway/service.js +27 -2
  109. package/dist/src/local-apps/preview-runtime-bridge.d.ts +1 -1
  110. package/dist/src/local-apps/preview-runtime-bridge.js +14 -11
  111. package/dist/src/mobile/notification-service.js +1 -0
  112. package/dist/src/pets/factory.js +23 -26
  113. package/dist/src/pets/manifest.d.ts +2 -1
  114. package/dist/src/pets/manifest.js +15 -8
  115. package/dist/src/pets/validator.js +8 -3
  116. package/dist/src/proactive/calendar-signals.d.ts +4 -0
  117. package/dist/src/proactive/calendar-signals.js +120 -0
  118. package/dist/src/proactive/focus-service.d.ts +62 -0
  119. package/dist/src/proactive/focus-service.js +173 -0
  120. package/dist/src/proactive/index.d.ts +7 -0
  121. package/dist/src/proactive/index.js +7 -0
  122. package/dist/src/proactive/insight-repository.d.ts +19 -0
  123. package/dist/src/proactive/insight-repository.js +106 -0
  124. package/dist/src/proactive/run-processor.d.ts +19 -0
  125. package/dist/src/proactive/run-processor.js +125 -0
  126. package/dist/src/proactive/trial-expiry-reconciler.d.ts +5 -0
  127. package/dist/src/proactive/trial-expiry-reconciler.js +30 -0
  128. package/dist/src/proactive/types.d.ts +64 -0
  129. package/dist/src/proactive/types.js +1 -0
  130. package/dist/src/proactive/watch-repository.d.ts +28 -0
  131. package/dist/src/proactive/watch-repository.js +129 -0
  132. package/dist/src/storage/sqlite/knowledge-repository.d.ts +1 -0
  133. package/dist/src/storage/sqlite/knowledge-repository.js +4 -0
  134. package/dist/src/storage/sqlite/migrations/047_work_discovery_sources.sql +16 -0
  135. package/dist/src/storage/sqlite/migrations/048_work_understanding_investigations.sql +40 -0
  136. package/dist/src/storage/sqlite/migrations/049_work_understanding_threads.sql +52 -0
  137. package/dist/src/storage/sqlite/migrations/050_work_discovery_source_refreshes.sql +15 -0
  138. package/dist/src/storage/sqlite/migrations/051_focus_watches.sql +26 -0
  139. package/dist/src/storage/sqlite/migrations/052_proactive_insights.sql +25 -0
  140. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  141. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  142. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  143. package/dist/src/storage/sqlite/schema.js +1 -1
  144. package/dist/src/work-discovery/analyzer.d.ts +12 -1
  145. package/dist/src/work-discovery/analyzer.js +126 -7
  146. package/dist/src/work-discovery/candidate-discovery.d.ts +17 -0
  147. package/dist/src/work-discovery/candidate-discovery.js +279 -0
  148. package/dist/src/work-discovery/evaluation.d.ts +11 -0
  149. package/dist/src/work-discovery/evaluation.js +17 -0
  150. package/dist/src/work-discovery/governance.d.ts +25 -0
  151. package/dist/src/work-discovery/governance.js +96 -0
  152. package/dist/src/work-discovery/incremental-repository.d.ts +13 -0
  153. package/dist/src/work-discovery/incremental-repository.js +55 -0
  154. package/dist/src/work-discovery/incremental.d.ts +2 -0
  155. package/dist/src/work-discovery/incremental.js +7 -0
  156. package/dist/src/work-discovery/index.d.ts +10 -0
  157. package/dist/src/work-discovery/index.js +13 -3
  158. package/dist/src/work-discovery/investigation-repository.d.ts +16 -0
  159. package/dist/src/work-discovery/investigation-repository.js +122 -0
  160. package/dist/src/work-discovery/investigator.d.ts +55 -0
  161. package/dist/src/work-discovery/investigator.js +358 -0
  162. package/dist/src/work-discovery/probe.d.ts +22 -0
  163. package/dist/src/work-discovery/probe.js +88 -1
  164. package/dist/src/work-discovery/service.d.ts +110 -1
  165. package/dist/src/work-discovery/service.js +578 -8
  166. package/dist/src/work-discovery/source-repository.d.ts +13 -0
  167. package/dist/src/work-discovery/source-repository.js +72 -0
  168. package/dist/src/work-discovery/thread-repository.d.ts +34 -0
  169. package/dist/src/work-discovery/thread-repository.js +112 -0
  170. package/dist/src/work-discovery/thread-service.d.ts +8 -0
  171. package/dist/src/work-discovery/thread-service.js +104 -0
  172. package/dist/src/work-discovery/types.d.ts +145 -0
  173. package/package.json +1 -1
  174. package/skills/creative/hatch-pet/SKILL.md +2 -2
  175. package/dist/gateway/static/root/assets/activity-store-C-57rvkX.js +0 -1
  176. package/dist/gateway/static/root/assets/agents-P05C4A-3.js +0 -2
  177. package/dist/gateway/static/root/assets/apps-page-DANGrBpN.js +0 -1
  178. package/dist/gateway/static/root/assets/automations-page-BXfGL_cm.js +0 -6
  179. package/dist/gateway/static/root/assets/browser-settings-page-534luV0B.js +0 -8
  180. package/dist/gateway/static/root/assets/channels-settings-M91zFJVi.js +0 -1
  181. package/dist/gateway/static/root/assets/channels-status-swr-vooEF4lG.js +0 -1
  182. package/dist/gateway/static/root/assets/check-BODqo66o.js +0 -1
  183. package/dist/gateway/static/root/assets/circle-alert-RzVebuX2.js +0 -1
  184. package/dist/gateway/static/root/assets/cn-BQ_8x0bT.js +0 -1
  185. package/dist/gateway/static/root/assets/connectors-page-CV5UulW6.js +0 -2
  186. package/dist/gateway/static/root/assets/desktop-pet-Cil2eSpW.js +0 -1
  187. package/dist/gateway/static/root/assets/desktop-pet-settings-DwsC6l5F.js +0 -1
  188. package/dist/gateway/static/root/assets/desktop-pet-sprite-DqZ6fbIj.js +0 -1
  189. package/dist/gateway/static/root/assets/gateway-settings-BpJ0v56l.js +0 -1
  190. package/dist/gateway/static/root/assets/goal-create-dialog-lMHD-2CX.js +0 -1
  191. package/dist/gateway/static/root/assets/goal-detail-page-yPmF-G7b.js +0 -4
  192. package/dist/gateway/static/root/assets/goals-page-BHTCKobG.js +0 -2
  193. package/dist/gateway/static/root/assets/index-BgZhEOkN.css +0 -1
  194. package/dist/gateway/static/root/assets/index-BuqGh2SW.js +0 -193
  195. package/dist/gateway/static/root/assets/local-app-workbench-page-zpp_wciE.js +0 -2
  196. package/dist/gateway/static/root/assets/logs-page-B4CRYqYR.js +0 -7
  197. package/dist/gateway/static/root/assets/messages-BuT0SR1R.js +0 -5
  198. package/dist/gateway/static/root/assets/models-hub-panel-1hdoAoB_.js +0 -2
  199. package/dist/gateway/static/root/assets/notes-workbench-DQEAZTA5.js +0 -4
  200. package/dist/gateway/static/root/assets/project-detail-page-Bp9bC7A6.js +0 -1
  201. package/dist/gateway/static/root/assets/projects-page-RNorFXTw.js +0 -1
  202. package/dist/gateway/static/root/assets/remote-access-hub-BqRTcM_6.js +0 -1
  203. package/dist/gateway/static/root/assets/sessions-page-B2EfW1cm.js +0 -1
  204. package/dist/gateway/static/root/assets/settings-advanced-gate-CklL85T4.js +0 -1
  205. package/dist/gateway/static/root/assets/setup-status-panel-D3my1HgB.js +0 -1
  206. package/dist/gateway/static/root/assets/shares-settings-DVxgjrsP.js +0 -1
  207. package/dist/gateway/static/root/assets/skill-reload-api-Bcu58ZaF.js +0 -1
  208. package/dist/gateway/static/root/assets/skills-page-CDd-84FE.js +0 -2
  209. package/dist/gateway/static/root/assets/sliding-segmented-uWmrxE3P.js +0 -1
  210. package/dist/gateway/static/root/assets/user-context-page-1OyJnTyR.js +0 -1
  211. package/dist/gateway/static/root/assets/voice-api-key-field-B2ncRwHS.js +0 -1
  212. package/dist/gateway/static/root/assets/work-discovery-page-BZlY4PZL.js +0 -1
  213. package/dist/gateway/static/root/assets/work-item-detail-page-DPXMrneY.js +0 -1
  214. package/dist/gateway/static/root/assets/work-page-CTERoqyv.js +0 -1
  215. package/dist/gateway/static/root/assets/workflows-page-CLgsnqFQ.js +0 -11
@@ -0,0 +1,279 @@
1
+ import { inferProjectKind } from "../projects/project-kind.js";
2
+ import "../projects/index.js";
3
+ import { homedir } from "node:os";
4
+ import { basename, extname, isAbsolute, join, resolve } from "node:path";
5
+ import { constants } from "node:fs";
6
+ import { execFile } from "node:child_process";
7
+ import { access, readFile, readdir, realpath, stat } from "node:fs/promises";
8
+ import { promisify } from "node:util";
9
+ //#region src/work-discovery/candidate-discovery.ts
10
+ const execFileAsync = promisify(execFile);
11
+ const COMMON_WORK_ROOT_NAMES = [
12
+ "develop",
13
+ "Developer",
14
+ "Projects",
15
+ "workspace",
16
+ "code",
17
+ "src"
18
+ ];
19
+ const PERSONAL_WORK_ROOT_NAMES = [
20
+ "Desktop",
21
+ "Documents",
22
+ "Downloads"
23
+ ];
24
+ const MAX_ROOT_CHILDREN = 100;
25
+ const GIT_TIMEOUT_MS = 2500;
26
+ const RECENT_GENERAL_WORK_MS = 90 * 864e5;
27
+ const GENERAL_WORK_EXTENSIONS = new Set([
28
+ ".doc",
29
+ ".docx",
30
+ ".md",
31
+ ".odt",
32
+ ".pdf",
33
+ ".ppt",
34
+ ".pptx",
35
+ ".rtf",
36
+ ".txt",
37
+ ".xls",
38
+ ".xlsx"
39
+ ]);
40
+ function abortIfNeeded(signal) {
41
+ if (signal?.aborted) throw new DOMException("Work discovery canceled", "AbortError");
42
+ }
43
+ async function readableDirectory(path) {
44
+ try {
45
+ const canonical = await realpath(path);
46
+ if (!(await stat(canonical)).isDirectory()) return null;
47
+ await access(canonical, constants.R_OK);
48
+ return canonical;
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+ async function gitSignal(rootPath, signal) {
54
+ try {
55
+ const [{ stdout: statusOutput }, { stdout: commitOutput }] = await Promise.all([execFileAsync("git", [
56
+ "-C",
57
+ rootPath,
58
+ "status",
59
+ "--short",
60
+ "--branch",
61
+ "--untracked-files=no"
62
+ ], {
63
+ timeout: GIT_TIMEOUT_MS,
64
+ maxBuffer: 128 * 1024,
65
+ signal
66
+ }), execFileAsync("git", [
67
+ "-C",
68
+ rootPath,
69
+ "log",
70
+ "-1",
71
+ "--format=%ct"
72
+ ], {
73
+ timeout: GIT_TIMEOUT_MS,
74
+ maxBuffer: 16 * 1024,
75
+ signal
76
+ })]);
77
+ const statusLines = statusOutput.split("\n").filter(Boolean);
78
+ const branch = statusLines[0]?.startsWith("## ") ? statusLines[0].slice(3).split("...")[0]?.trim() : void 0;
79
+ const timestamp = Number(commitOutput.trim()) * 1e3;
80
+ return {
81
+ ...branch ? { branch } : {},
82
+ changedFileCount: Math.min(100, Math.max(0, statusLines.length - 1)),
83
+ ...Number.isFinite(timestamp) && timestamp > 0 ? { lastCommitAt: timestamp } : {}
84
+ };
85
+ } catch {
86
+ return { changedFileCount: 0 };
87
+ }
88
+ }
89
+ async function recentGeneralWorkAt(rootPath, nowMs) {
90
+ const entries = await readdir(rootPath, { withFileTypes: true }).catch(() => []);
91
+ let latest;
92
+ for (const entry of entries.slice(0, MAX_ROOT_CHILDREN)) {
93
+ if (!entry.isFile() || entry.name.startsWith(".") || !GENERAL_WORK_EXTENSIONS.has(extname(entry.name).toLowerCase())) continue;
94
+ const info = await stat(join(rootPath, entry.name)).catch(() => null);
95
+ if (!info?.isFile() || nowMs - info.mtimeMs > RECENT_GENERAL_WORK_MS) continue;
96
+ latest = latest == null ? info.mtimeMs : Math.max(latest, info.mtimeMs);
97
+ }
98
+ return latest;
99
+ }
100
+ async function isCodingRoot(rootPath) {
101
+ if (inferProjectKind({ workspaceRoot: rootPath }).kind === "coding") return true;
102
+ return stat(join(rootPath, ".git")).then(() => true).catch(() => false);
103
+ }
104
+ function recencyScore(lastActiveAt, nowMs) {
105
+ if (!lastActiveAt) return 0;
106
+ const ageDays = Math.max(0, (nowMs - lastActiveAt) / 864e5);
107
+ if (ageDays <= 2) return 40;
108
+ if (ageDays <= 7) return 34;
109
+ if (ageDays <= 14) return 27;
110
+ if (ageDays <= 30) return 19;
111
+ if (ageDays <= 90) return 10;
112
+ return 2;
113
+ }
114
+ async function probeCandidate(input, nowMs, signal) {
115
+ abortIfNeeded(signal);
116
+ const rootPath = await readableDirectory(input.rootPath);
117
+ if (!rootPath) return null;
118
+ const [rootInfo, git, personalWorkAt] = await Promise.all([
119
+ stat(rootPath),
120
+ gitSignal(rootPath, signal),
121
+ input.source === "personal_work_root" ? recentGeneralWorkAt(rootPath, nowMs) : void 0
122
+ ]);
123
+ abortIfNeeded(signal);
124
+ const inference = inferProjectKind({ workspaceRoot: rootPath });
125
+ if (input.source === "common_work_root" && !git.lastCommitAt && inference.kind !== "coding") return null;
126
+ if (input.source === "personal_work_root" && !git.lastCommitAt && inference.kind !== "coding" && !personalWorkAt) return null;
127
+ const lastActiveAt = Math.max(rootInfo.mtimeMs, git.lastCommitAt ?? 0, personalWorkAt ?? 0) || void 0;
128
+ const score = Math.min(100, Math.round(recencyScore(lastActiveAt, nowMs) + (git.lastCommitAt ? 20 : 0) + Math.min(10, git.changedFileCount * 2) + Math.round(inference.confidence * 20)));
129
+ const evidence = [];
130
+ if (git.lastCommitAt) evidence.push("recent Git activity");
131
+ if (git.changedFileCount > 0) evidence.push(`${git.changedFileCount} changed files`);
132
+ if (inference.reasons[0]) evidence.push(inference.reasons[0]);
133
+ if (input.source === "existing_project") evidence.push("already connected to xopc");
134
+ if (input.source === "approved_directory") evidence.push("previously approved work folder");
135
+ return {
136
+ id: input.projectId ? `project:${input.projectId}` : `path:${rootPath}`,
137
+ rootPath,
138
+ displayName: basename(rootPath),
139
+ source: input.source,
140
+ ...input.projectId ? { projectId: input.projectId } : {},
141
+ projectKind: inference.kind,
142
+ projectKindConfidence: inference.confidence,
143
+ score,
144
+ ...lastActiveAt ? { lastActiveAt } : {},
145
+ ...git.branch ? { branch: git.branch } : {},
146
+ changedFileCount: git.changedFileCount,
147
+ evidence: evidence.slice(0, 4)
148
+ };
149
+ }
150
+ async function commonRootInputs(homeDirectory, signal) {
151
+ const inputs = [];
152
+ for (const name of COMMON_WORK_ROOT_NAMES) {
153
+ abortIfNeeded(signal);
154
+ const commonRoot = await readableDirectory(join(homeDirectory, name));
155
+ if (!commonRoot) continue;
156
+ let entries;
157
+ try {
158
+ entries = await readdir(commonRoot, { withFileTypes: true });
159
+ } catch {
160
+ continue;
161
+ }
162
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name)).slice(0, MAX_ROOT_CHILDREN)) {
163
+ if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
164
+ const directPath = join(commonRoot, entry.name);
165
+ if (await isCodingRoot(directPath)) {
166
+ inputs.push({
167
+ rootPath: directPath,
168
+ source: "common_work_root"
169
+ });
170
+ continue;
171
+ }
172
+ const nestedEntries = await readdir(directPath, { withFileTypes: true }).catch(() => []);
173
+ for (const nested of nestedEntries.sort((left, right) => left.name.localeCompare(right.name)).slice(0, MAX_ROOT_CHILDREN)) {
174
+ abortIfNeeded(signal);
175
+ if (!nested.isDirectory() || nested.name.startsWith(".")) continue;
176
+ const nestedPath = join(directPath, nested.name);
177
+ if (await isCodingRoot(nestedPath)) inputs.push({
178
+ rootPath: nestedPath,
179
+ source: "common_work_root"
180
+ });
181
+ }
182
+ }
183
+ }
184
+ return inputs;
185
+ }
186
+ function resolveXdgPath(value, homeDirectory) {
187
+ const expanded = value.trim().replace(/^"|"$/g, "").replace(/^\$HOME(?=\/|$)|^\$\{HOME\}(?=\/|$)/, homeDirectory);
188
+ return isAbsolute(expanded) ? resolve(expanded) : null;
189
+ }
190
+ async function personalWorkRoots(homeDirectory, platform, environment) {
191
+ const roots = PERSONAL_WORK_ROOT_NAMES.map((name) => join(homeDirectory, name));
192
+ if (platform === "win32") for (const oneDrive of [
193
+ environment.OneDrive,
194
+ environment.OneDriveConsumer,
195
+ environment.OneDriveCommercial
196
+ ]) {
197
+ if (!oneDrive) continue;
198
+ roots.push(...PERSONAL_WORK_ROOT_NAMES.map((name) => join(oneDrive, name)));
199
+ }
200
+ if (platform === "linux") {
201
+ const config = await readFile(join(homeDirectory, ".config", "user-dirs.dirs"), "utf8").catch(() => "");
202
+ for (const line of config.split("\n")) {
203
+ const match = /^XDG_(?:DESKTOP|DOCUMENTS|DOWNLOAD)_DIR=(.+)$/.exec(line.trim());
204
+ const path = match?.[1] ? resolveXdgPath(match[1], homeDirectory) : null;
205
+ if (path) roots.push(path);
206
+ }
207
+ }
208
+ return [...new Set(roots)];
209
+ }
210
+ async function personalRootInputs(roots, nowMs, signal) {
211
+ const inputs = [];
212
+ for (const root of roots) {
213
+ abortIfNeeded(signal);
214
+ const readableRoot = await readableDirectory(root);
215
+ if (!readableRoot) continue;
216
+ const entries = await readdir(readableRoot, { withFileTypes: true }).catch(() => []);
217
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name)).slice(0, MAX_ROOT_CHILDREN)) {
218
+ abortIfNeeded(signal);
219
+ if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
220
+ const directPath = join(readableRoot, entry.name);
221
+ if (await isCodingRoot(directPath) || await recentGeneralWorkAt(directPath, nowMs)) {
222
+ inputs.push({
223
+ rootPath: directPath,
224
+ source: "personal_work_root"
225
+ });
226
+ continue;
227
+ }
228
+ const nestedEntries = await readdir(directPath, { withFileTypes: true }).catch(() => []);
229
+ for (const nested of nestedEntries.sort((left, right) => left.name.localeCompare(right.name)).slice(0, MAX_ROOT_CHILDREN)) {
230
+ if (!nested.isDirectory() || nested.name.startsWith(".")) continue;
231
+ const nestedPath = join(directPath, nested.name);
232
+ if (await isCodingRoot(nestedPath) || await recentGeneralWorkAt(nestedPath, nowMs)) inputs.push({
233
+ rootPath: nestedPath,
234
+ source: "personal_work_root"
235
+ });
236
+ }
237
+ }
238
+ }
239
+ return inputs;
240
+ }
241
+ async function discoverWorkCandidates(options = {}) {
242
+ const nowMs = options.nowMs ?? Date.now();
243
+ const homeDirectory = options.homeDirectory ?? homedir();
244
+ const existingInputs = (options.existingProjects ?? []).flatMap((project) => {
245
+ const rootPath = project.workspaceRoot?.trim();
246
+ return rootPath ? [{
247
+ rootPath,
248
+ source: "existing_project",
249
+ projectId: project.id
250
+ }] : [];
251
+ });
252
+ const approvedInputs = (options.approvedDirectories ?? []).map((source) => ({
253
+ rootPath: source.rootPath,
254
+ source: "approved_directory"
255
+ }));
256
+ const [commonInputs, personalInputs] = await Promise.all([commonRootInputs(homeDirectory, options.signal), personalWorkRoots(homeDirectory, options.platform ?? process.platform, options.environment ?? process.env).then((roots) => personalRootInputs(roots, nowMs, options.signal))]);
257
+ const uniqueInputs = /* @__PURE__ */ new Map();
258
+ for (const input of [
259
+ ...existingInputs,
260
+ ...approvedInputs,
261
+ ...commonInputs,
262
+ ...personalInputs
263
+ ]) {
264
+ const existing = uniqueInputs.get(input.rootPath);
265
+ if (!existing || input.source === "existing_project" || input.source === "approved_directory" && existing.source !== "existing_project") uniqueInputs.set(input.rootPath, input);
266
+ }
267
+ const candidates = [];
268
+ const inputs = [...uniqueInputs.values()];
269
+ for (let index = 0; index < inputs.length; index += 4) {
270
+ abortIfNeeded(options.signal);
271
+ const batch = await Promise.all(inputs.slice(index, index + 4).map((input) => probeCandidate(input, nowMs, options.signal)));
272
+ candidates.push(...batch.filter((item) => Boolean(item)));
273
+ }
274
+ const canonicalCandidates = /* @__PURE__ */ new Map();
275
+ for (const candidate of candidates) if (!canonicalCandidates.get(candidate.rootPath) || candidate.source === "existing_project") canonicalCandidates.set(candidate.rootPath, candidate);
276
+ return [...canonicalCandidates.values()].sort((a, b) => b.score - a.score || (b.lastActiveAt ?? 0) - (a.lastActiveAt ?? 0) || a.rootPath.localeCompare(b.rootPath)).slice(0, 8);
277
+ }
278
+ //#endregion
279
+ export { discoverWorkCandidates };
@@ -0,0 +1,11 @@
1
+ export interface WorkUnderstandingEvaluationCase {
2
+ expectedThreadKeys: string[];
3
+ inferredThreadKeys: string[];
4
+ evidenceBackedThreadKeys: string[];
5
+ }
6
+ export declare function evaluateWorkUnderstandingCase(input: WorkUnderstandingEvaluationCase): {
7
+ precision: number;
8
+ recall: number;
9
+ evidenceCoverage: number;
10
+ f1: number;
11
+ };
@@ -0,0 +1,17 @@
1
+ //#region src/work-discovery/evaluation.ts
2
+ function evaluateWorkUnderstandingCase(input) {
3
+ const expected = new Set(input.expectedThreadKeys);
4
+ const inferred = new Set(input.inferredThreadKeys);
5
+ const evidenceBacked = new Set(input.evidenceBackedThreadKeys);
6
+ const truePositives = [...inferred].filter((key) => expected.has(key)).length;
7
+ const precision = inferred.size ? truePositives / inferred.size : expected.size ? 0 : 1;
8
+ const recall = expected.size ? truePositives / expected.size : 1;
9
+ return {
10
+ precision,
11
+ recall,
12
+ evidenceCoverage: inferred.size ? [...inferred].filter((key) => evidenceBacked.has(key)).length / inferred.size : 1,
13
+ f1: precision + recall ? 2 * precision * recall / (precision + recall) : 0
14
+ };
15
+ }
16
+ //#endregion
17
+ export { evaluateWorkUnderstandingCase };
@@ -0,0 +1,25 @@
1
+ export interface WorkUnderstandingSourceLineage {
2
+ sourceId: string;
3
+ evidenceCount: number;
4
+ threadCount: number;
5
+ memoryRecordCount: number;
6
+ discoveryRunCount: number;
7
+ }
8
+ export declare function getWorkUnderstandingSourceLineage(sourceId: string): WorkUnderstandingSourceLineage | null;
9
+ export declare function deleteWorkUnderstandingDerivedData(sourceId: string): {
10
+ deletedMemoryRecords: number;
11
+ evidenceCount: number | bigint;
12
+ deletedThreads: number;
13
+ retainedThreads: number;
14
+ };
15
+ export declare function getWorkUnderstandingMetrics(): {
16
+ completedInvestigations: number;
17
+ averageToolCalls: number;
18
+ activeThreads: number;
19
+ confirmedThreads: number;
20
+ recognitionConfirmationRate: number;
21
+ evidenceBySource: {
22
+ sourceType: string;
23
+ count: number;
24
+ }[];
25
+ };
@@ -0,0 +1,96 @@
1
+ import { requireXopcDatabase } from "../storage/sqlite/connection.js";
2
+ import { runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
3
+ import { deleteMemoryRecord, listMemoryRecords } from "../storage/sqlite/memory-records-repository.js";
4
+ //#region src/work-discovery/governance.ts
5
+ function memoryRecordsForSource(sourceId, rootPath) {
6
+ const { db } = requireXopcDatabase();
7
+ const runIds = rootPath ? db.prepare("SELECT id FROM work_discovery_runs WHERE root_path = ?").all(rootPath).map((row) => row.id) : [];
8
+ const runPaths = new Set(runIds.map((id) => `work-discovery://${id}`));
9
+ const records = [];
10
+ for (let offset = 0;; offset += 500) {
11
+ const page = listMemoryRecords({
12
+ limit: 500,
13
+ offset
14
+ });
15
+ records.push(...page.filter((record) => runPaths.has(record.source.path)));
16
+ if (page.length < 500) break;
17
+ }
18
+ return records;
19
+ }
20
+ function getWorkUnderstandingSourceLineage(sourceId) {
21
+ const { db } = requireXopcDatabase();
22
+ const source = db.prepare("SELECT root_path FROM work_discovery_sources WHERE source_id = ?").get(sourceId);
23
+ if (!source) return null;
24
+ const evidenceCount = db.prepare("SELECT COUNT(*) AS count FROM work_understanding_evidence WHERE source_grant_id = ?").get(sourceId).count;
25
+ const threadCount = db.prepare(`SELECT COUNT(DISTINCT te.thread_id) AS count
26
+ FROM work_understanding_thread_evidence te
27
+ JOIN work_understanding_evidence e ON e.evidence_id = te.evidence_id
28
+ WHERE e.source_grant_id = ?`).get(sourceId).count;
29
+ const discoveryRunCount = source.root_path ? db.prepare("SELECT COUNT(*) AS count FROM work_discovery_runs WHERE root_path = ?").get(source.root_path).count : 0;
30
+ return {
31
+ sourceId,
32
+ evidenceCount,
33
+ threadCount,
34
+ memoryRecordCount: memoryRecordsForSource(sourceId, source.root_path).length,
35
+ discoveryRunCount
36
+ };
37
+ }
38
+ function deleteWorkUnderstandingDerivedData(sourceId) {
39
+ if (!getWorkUnderstandingSourceLineage(sourceId)) return null;
40
+ const { db } = requireXopcDatabase();
41
+ const memoryRecords = memoryRecordsForSource(sourceId, db.prepare("SELECT root_path FROM work_discovery_sources WHERE source_id = ?").get(sourceId).root_path).filter((record) => record.explicitness !== "explicit");
42
+ const affectedThreadIds = db.prepare(`SELECT DISTINCT te.thread_id AS id
43
+ FROM work_understanding_thread_evidence te
44
+ JOIN work_understanding_evidence e ON e.evidence_id = te.evidence_id
45
+ WHERE e.source_grant_id = ?`).all(sourceId).map((row) => row.id);
46
+ const result = runSqliteWriteTransaction((transaction) => {
47
+ transaction.prepare(`DELETE FROM work_understanding_thread_evidence
48
+ WHERE evidence_id IN (SELECT evidence_id FROM work_understanding_evidence WHERE source_grant_id = ?)`).run(sourceId);
49
+ const evidence = transaction.prepare("DELETE FROM work_understanding_evidence WHERE source_grant_id = ?").run(sourceId);
50
+ let deletedThreads = 0;
51
+ let retainedThreads = 0;
52
+ for (const threadId of affectedThreadIds) {
53
+ if (transaction.prepare("SELECT COUNT(*) AS count FROM work_understanding_thread_evidence WHERE thread_id = ?").get(threadId).count > 0) continue;
54
+ const thread = transaction.prepare("SELECT user_status, status FROM work_understanding_threads WHERE thread_id = ?").get(threadId);
55
+ if (!thread) continue;
56
+ if (thread.user_status === "confirmed" || thread.user_status === "corrected") {
57
+ transaction.prepare(`UPDATE work_understanding_threads SET confidence = MIN(confidence, 0.5),
58
+ status = CASE WHEN status IN ('paused', 'completed') THEN status ELSE 'uncertain' END,
59
+ updated_at = ? WHERE thread_id = ?`).run(Date.now(), threadId);
60
+ retainedThreads += 1;
61
+ } else {
62
+ transaction.prepare("DELETE FROM work_understanding_threads WHERE thread_id = ?").run(threadId);
63
+ deletedThreads += 1;
64
+ }
65
+ }
66
+ return {
67
+ evidenceCount: evidence.changes,
68
+ deletedThreads,
69
+ retainedThreads
70
+ };
71
+ });
72
+ const deletedMemoryRecords = memoryRecords.filter((record) => deleteMemoryRecord(record.id)).length;
73
+ return {
74
+ ...result,
75
+ deletedMemoryRecords
76
+ };
77
+ }
78
+ function getWorkUnderstandingMetrics() {
79
+ const { db } = requireXopcDatabase();
80
+ const scalar = (sql) => db.prepare(sql).get().value;
81
+ const evidenceBySource = db.prepare("SELECT source_type AS sourceType, COUNT(*) AS count FROM work_understanding_evidence GROUP BY source_type").all();
82
+ const recognition = db.prepare(`SELECT recognition_decision AS decision, COUNT(*) AS count
83
+ FROM work_discovery_feedback GROUP BY recognition_decision`).all();
84
+ const reviewed = recognition.reduce((sum, item) => sum + (item.decision === "dismissed" ? 0 : item.count), 0);
85
+ const confirmed = recognition.find((item) => item.decision === "confirmed")?.count ?? 0;
86
+ return {
87
+ completedInvestigations: scalar("SELECT COUNT(*) AS value FROM work_understanding_investigations WHERE status = 'completed'"),
88
+ averageToolCalls: scalar("SELECT COALESCE(AVG(tool_call_count), 0) AS value FROM work_understanding_investigations"),
89
+ activeThreads: scalar("SELECT COUNT(*) AS value FROM work_understanding_threads WHERE status IN ('active', 'blocked') AND user_status <> 'rejected'"),
90
+ confirmedThreads: scalar("SELECT COUNT(*) AS value FROM work_understanding_threads WHERE user_status IN ('confirmed', 'corrected')"),
91
+ recognitionConfirmationRate: reviewed ? confirmed / reviewed : 0,
92
+ evidenceBySource
93
+ };
94
+ }
95
+ //#endregion
96
+ export { deleteWorkUnderstandingDerivedData, getWorkUnderstandingMetrics, getWorkUnderstandingSourceLineage };
@@ -0,0 +1,13 @@
1
+ import type { WorkDiscoveryPreview, WorkDiscoverySourceRefresh } from './types.js';
2
+ export declare function recordWorkDiscoverySourceRefresh(input: {
3
+ sourceId: string;
4
+ changed: boolean;
5
+ previousFingerprint?: WorkDiscoveryPreview['fingerprint'];
6
+ currentFingerprint: WorkDiscoveryPreview['fingerprint'];
7
+ status?: WorkDiscoverySourceRefresh['status'];
8
+ discoveryRunId?: string;
9
+ checkedAt?: number;
10
+ }): WorkDiscoverySourceRefresh;
11
+ export declare function listWorkDiscoverySourceRefreshes(sourceId: string, limit?: number): WorkDiscoverySourceRefresh[];
12
+ export declare function findActiveWorkDiscoverySourceRefresh(sourceId: string, fingerprint: WorkDiscoveryPreview['fingerprint']): WorkDiscoverySourceRefresh | null;
13
+ export declare function updateWorkDiscoverySourceRefreshForRun(runId: string, status: 'completed' | 'failed'): void;
@@ -0,0 +1,55 @@
1
+ import { requireXopcDatabase } from "../storage/sqlite/connection.js";
2
+ import { runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
3
+ import { workDiscoveryFingerprintsEqual } from "./incremental.js";
4
+ import { randomUUID } from "node:crypto";
5
+ //#region src/work-discovery/incremental-repository.ts
6
+ function parseFingerprint(value) {
7
+ if (!value) return void 0;
8
+ try {
9
+ return JSON.parse(value);
10
+ } catch {
11
+ return;
12
+ }
13
+ }
14
+ function fromRow(row) {
15
+ return {
16
+ id: row.refresh_id,
17
+ sourceId: row.source_id,
18
+ changed: row.changed === 1,
19
+ ...parseFingerprint(row.previous_fingerprint_json) ? { previousFingerprint: parseFingerprint(row.previous_fingerprint_json) } : {},
20
+ currentFingerprint: parseFingerprint(row.current_fingerprint_json),
21
+ status: row.status,
22
+ ...row.discovery_run_id ? { discoveryRunId: row.discovery_run_id } : {},
23
+ checkedAt: row.checked_at
24
+ };
25
+ }
26
+ function recordWorkDiscoverySourceRefresh(input) {
27
+ const id = randomUUID();
28
+ runSqliteWriteTransaction((db) => {
29
+ db.prepare(`INSERT INTO work_discovery_source_refreshes (
30
+ refresh_id, source_id, discovery_run_id, changed, previous_fingerprint_json,
31
+ current_fingerprint_json, status, checked_at
32
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.sourceId, input.discoveryRunId ?? null, input.changed ? 1 : 0, input.previousFingerprint ? JSON.stringify(input.previousFingerprint) : null, JSON.stringify(input.currentFingerprint), input.status ?? "checked", input.checkedAt ?? Date.now());
33
+ });
34
+ const { db } = requireXopcDatabase();
35
+ return fromRow(db.prepare("SELECT * FROM work_discovery_source_refreshes WHERE refresh_id = ?").get(id));
36
+ }
37
+ function listWorkDiscoverySourceRefreshes(sourceId, limit = 20) {
38
+ const { db } = requireXopcDatabase();
39
+ return db.prepare("SELECT * FROM work_discovery_source_refreshes WHERE source_id = ? ORDER BY checked_at DESC LIMIT ?").all(sourceId, Math.max(1, Math.min(100, limit))).map(fromRow);
40
+ }
41
+ function findActiveWorkDiscoverySourceRefresh(sourceId, fingerprint) {
42
+ const { db } = requireXopcDatabase();
43
+ return db.prepare(`SELECT refresh.* FROM work_discovery_source_refreshes refresh
44
+ JOIN work_discovery_runs run ON run.id = refresh.discovery_run_id
45
+ WHERE refresh.source_id = ? AND refresh.status = 'queued'
46
+ AND run.status IN ('queued', 'probing', 'analyzing')
47
+ ORDER BY refresh.checked_at DESC`).all(sourceId).map(fromRow).find((refresh) => workDiscoveryFingerprintsEqual(refresh.currentFingerprint, fingerprint)) ?? null;
48
+ }
49
+ function updateWorkDiscoverySourceRefreshForRun(runId, status) {
50
+ runSqliteWriteTransaction((db) => {
51
+ db.prepare("UPDATE work_discovery_source_refreshes SET status = ? WHERE discovery_run_id = ?").run(status, runId);
52
+ });
53
+ }
54
+ //#endregion
55
+ export { findActiveWorkDiscoverySourceRefresh, listWorkDiscoverySourceRefreshes, recordWorkDiscoverySourceRefresh, updateWorkDiscoverySourceRefreshForRun };
@@ -0,0 +1,2 @@
1
+ import type { WorkDiscoveryPreview } from './types.js';
2
+ export declare function workDiscoveryFingerprintsEqual(left: WorkDiscoveryPreview['fingerprint'] | undefined, right: WorkDiscoveryPreview['fingerprint']): boolean;
@@ -0,0 +1,7 @@
1
+ //#region src/work-discovery/incremental.ts
2
+ function workDiscoveryFingerprintsEqual(left, right) {
3
+ if (!left) return false;
4
+ return left.branch === right.branch && left.changedFileCount === right.changedFileCount && left.contentSignature === right.contentSignature && JSON.stringify(left.recentAreas) === JSON.stringify(right.recentAreas);
5
+ }
6
+ //#endregion
7
+ export { workDiscoveryFingerprintsEqual };
@@ -2,4 +2,14 @@ export * from './types.js';
2
2
  export * from './repository.js';
3
3
  export * from './probe.js';
4
4
  export * from './analyzer.js';
5
+ export * from './candidate-discovery.js';
5
6
  export * from './service.js';
7
+ export * from './source-repository.js';
8
+ export * from './investigation-repository.js';
9
+ export * from './investigator.js';
10
+ export * from './incremental.js';
11
+ export * from './incremental-repository.js';
12
+ export * from './governance.js';
13
+ export * from './evaluation.js';
14
+ export * from './thread-repository.js';
15
+ export * from './thread-service.js';
@@ -1,5 +1,15 @@
1
- import { WORK_DISCOVERY_SCAN_POLICY_VERSION, canonicalWorkDiscoveryRoot, previewWorkDiscoveryRoot, probeWorkDiscoveryRoot, summarizeWorkContextSnapshot } from "./probe.js";
2
- import { analyzeWorkContext, workDiscoveryResultMarkdown } from "./analyzer.js";
1
+ import { addWorkUnderstandingThreadFeedback, attachWorkUnderstandingThreadEvidence, getWorkUnderstandingThread, getWorkUnderstandingThreadByCanonicalKey, listWorkUnderstandingThreads, upsertWorkUnderstandingThread } from "./thread-repository.js";
2
+ import { WORK_DISCOVERY_SCAN_POLICY_VERSION, canonicalWorkDiscoveryRoot, previewWorkDiscoveryRoot, probeWorkDiscoveryRoot, readWorkDiscoveryTextExcerpt, searchWorkDiscoveryText, summarizeWorkContextSnapshot } from "./probe.js";
3
+ import { analyzePersonalContext, analyzeWorkContext, workDiscoveryResultMarkdown } from "./analyzer.js";
4
+ import { discoverWorkCandidates } from "./candidate-discovery.js";
5
+ import { deleteWorkUnderstandingDerivedData, getWorkUnderstandingMetrics, getWorkUnderstandingSourceLineage } from "./governance.js";
6
+ import { workDiscoveryFingerprintsEqual } from "./incremental.js";
7
+ import { findActiveWorkDiscoverySourceRefresh, listWorkDiscoverySourceRefreshes, recordWorkDiscoverySourceRefresh, updateWorkDiscoverySourceRefreshForRun } from "./incremental-repository.js";
8
+ import { appendWorkUnderstandingEvidence, createWorkUnderstandingInvestigation, getWorkUnderstandingInvestigation, getWorkUnderstandingInvestigationForRun, listWorkUnderstandingEvidence, resetWorkUnderstandingInvestigation, updateWorkUnderstandingEvidenceObservation, updateWorkUnderstandingInvestigation } from "./investigation-repository.js";
9
+ import { DEFAULT_WORK_INVESTIGATION_BUDGET, investigateWorkContext } from "./investigator.js";
10
+ import { persistWorkThreadsFromDiscovery } from "./thread-service.js";
3
11
  import { createWorkDiscoveryRun, getWorkDiscoveryOnboardingState, getWorkDiscoveryRun, getWorkDiscoveryRunByIdempotencyKey, setWorkDiscoveryFeedback, setWorkDiscoveryOnboardingState, updateWorkDiscoveryRun } from "./repository.js";
12
+ import { getWorkDiscoveryDirectorySource, listWorkDiscoveryDirectorySources, revokeWorkDiscoveryDirectorySource, upsertWorkDiscoveryDirectorySource } from "./source-repository.js";
4
13
  import { WorkDiscoveryService } from "./service.js";
5
- export { WORK_DISCOVERY_SCAN_POLICY_VERSION, WorkDiscoveryService, analyzeWorkContext, canonicalWorkDiscoveryRoot, createWorkDiscoveryRun, getWorkDiscoveryOnboardingState, getWorkDiscoveryRun, getWorkDiscoveryRunByIdempotencyKey, previewWorkDiscoveryRoot, probeWorkDiscoveryRoot, setWorkDiscoveryFeedback, setWorkDiscoveryOnboardingState, summarizeWorkContextSnapshot, updateWorkDiscoveryRun, workDiscoveryResultMarkdown };
14
+ import { evaluateWorkUnderstandingCase } from "./evaluation.js";
15
+ export { DEFAULT_WORK_INVESTIGATION_BUDGET, WORK_DISCOVERY_SCAN_POLICY_VERSION, WorkDiscoveryService, addWorkUnderstandingThreadFeedback, analyzePersonalContext, analyzeWorkContext, appendWorkUnderstandingEvidence, attachWorkUnderstandingThreadEvidence, canonicalWorkDiscoveryRoot, createWorkDiscoveryRun, createWorkUnderstandingInvestigation, deleteWorkUnderstandingDerivedData, discoverWorkCandidates, evaluateWorkUnderstandingCase, findActiveWorkDiscoverySourceRefresh, getWorkDiscoveryDirectorySource, getWorkDiscoveryOnboardingState, getWorkDiscoveryRun, getWorkDiscoveryRunByIdempotencyKey, getWorkUnderstandingInvestigation, getWorkUnderstandingInvestigationForRun, getWorkUnderstandingMetrics, getWorkUnderstandingSourceLineage, getWorkUnderstandingThread, getWorkUnderstandingThreadByCanonicalKey, investigateWorkContext, listWorkDiscoveryDirectorySources, listWorkDiscoverySourceRefreshes, listWorkUnderstandingEvidence, listWorkUnderstandingThreads, persistWorkThreadsFromDiscovery, previewWorkDiscoveryRoot, probeWorkDiscoveryRoot, readWorkDiscoveryTextExcerpt, recordWorkDiscoverySourceRefresh, resetWorkUnderstandingInvestigation, revokeWorkDiscoveryDirectorySource, searchWorkDiscoveryText, setWorkDiscoveryFeedback, setWorkDiscoveryOnboardingState, summarizeWorkContextSnapshot, updateWorkDiscoveryRun, updateWorkDiscoverySourceRefreshForRun, updateWorkUnderstandingEvidenceObservation, updateWorkUnderstandingInvestigation, upsertWorkDiscoveryDirectorySource, upsertWorkUnderstandingThread, workDiscoveryFingerprintsEqual, workDiscoveryResultMarkdown };
@@ -0,0 +1,16 @@
1
+ import type { WorkUnderstandingEvidenceItem, WorkUnderstandingInvestigation, WorkUnderstandingInvestigationBudget } from './types.js';
2
+ export declare function createWorkUnderstandingInvestigation(input: {
3
+ discoveryRunId: string;
4
+ budget: WorkUnderstandingInvestigationBudget;
5
+ nowMs?: number;
6
+ }): WorkUnderstandingInvestigation;
7
+ export declare function getWorkUnderstandingInvestigation(id: string): WorkUnderstandingInvestigation | null;
8
+ export declare function getWorkUnderstandingInvestigationForRun(runId: string): WorkUnderstandingInvestigation | null;
9
+ export declare function updateWorkUnderstandingInvestigation(id: string, patch: Partial<Pick<WorkUnderstandingInvestigation, 'status' | 'plan' | 'toolCallCount' | 'contentCharsRead' | 'completedAt' | 'errorMessage'>>): WorkUnderstandingInvestigation | null;
10
+ export declare function resetWorkUnderstandingInvestigation(id: string, budget: WorkUnderstandingInvestigationBudget, nowMs?: number): WorkUnderstandingInvestigation | null;
11
+ export declare function appendWorkUnderstandingEvidence(input: Omit<WorkUnderstandingEvidenceItem, 'id' | 'collectedAt'> & {
12
+ id?: string;
13
+ collectedAt?: number;
14
+ }): WorkUnderstandingEvidenceItem;
15
+ export declare function updateWorkUnderstandingEvidenceObservation(id: string, observation: string): void;
16
+ export declare function listWorkUnderstandingEvidence(investigationId: string): WorkUnderstandingEvidenceItem[];