@xopcai/xopc 0.0.283 → 0.0.285

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 (234) 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/{activity-store-iPtG94qr.js → activity-store-B0BTIk9j.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DQ74xqLi.js → agent-avatar-dicebear-Dc-0QZTQ.js} +1 -1
  5. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-CcmqK1Sc.js +1 -0
  6. package/dist/gateway/static/root/assets/agents-B_7PVt7S.js +1 -0
  7. package/dist/gateway/static/root/assets/api-COG_Tzpv.js +1 -0
  8. package/dist/gateway/static/root/assets/{api-KMJzMqvZ.js → api-Coly9ab6.js} +1 -1
  9. package/dist/gateway/static/root/assets/{app-management-settings-panel-B42TpqIb.js → app-management-settings-panel-TWBO-msg.js} +1 -1
  10. package/dist/gateway/static/root/assets/{appearance-settings-D9Lfe7Sk.js → appearance-settings-CpTZ2KuT.js} +1 -1
  11. package/dist/gateway/static/root/assets/apps-page-6qxxKFsK.js +1 -0
  12. package/dist/gateway/static/root/assets/{archive-plugin-BdmQwj4x.js → archive-plugin-z_u6alob.js} +1 -1
  13. package/dist/gateway/static/root/assets/{attachment-load-B4S0_nji.js → attachment-load-C2nS04TC.js} +1 -1
  14. package/dist/gateway/static/root/assets/{automations-page-B3TmHNjQ.js → automations-page-aIBsw9zR.js} +1 -1
  15. package/dist/gateway/static/root/assets/automations-workspace-BRQlU4qk.js +6 -0
  16. package/dist/gateway/static/root/assets/{binary-plugins-B3cXF_DP.js → binary-plugins-CGmXoZnl.js} +1 -1
  17. package/dist/gateway/static/root/assets/{block-editor-05zyBXBe.js → block-editor-CtvzCZhC.js} +3 -3
  18. package/dist/gateway/static/root/assets/{browser-automation-inputs-BTIufVbM.js → browser-automation-inputs-BOTU1isH.js} +1 -1
  19. package/dist/gateway/static/root/assets/{browser-automations-page-DRBA9nwe.js → browser-automations-page-BwU5V8J0.js} +2 -2
  20. package/dist/gateway/static/root/assets/browser-settings-page-DDjbDeHQ.js +8 -0
  21. package/dist/gateway/static/root/assets/capabilities-settings-panel-DM1Bcf1x.js +8 -0
  22. package/dist/gateway/static/root/assets/channels-settings-Bq36CUxL.js +1 -0
  23. package/dist/gateway/static/root/assets/{channels-status-swr-DMYQNs-J.js → channels-status-swr-Dfi0Desh.js} +1 -1
  24. package/dist/gateway/static/root/assets/{chat-terminal-dock-Dx5KQyo5.js → chat-terminal-dock-B0MvcO6j.js} +2 -2
  25. package/dist/gateway/static/root/assets/connectors-page-L8JehrxF.js +2 -0
  26. package/dist/gateway/static/root/assets/{date-picker-CkDROZoe.js → date-picker-DXOvbHCr.js} +1 -1
  27. package/dist/gateway/static/root/assets/{dependency-picker-Bl8HcvzL.js → dependency-picker-BEnC-eS0.js} +1 -1
  28. package/dist/gateway/static/root/assets/{desktop-pet-BcknAjbW.js → desktop-pet-DtPNvznL.js} +1 -1
  29. package/dist/gateway/static/root/assets/{desktop-pet-settings-BuCpXQPm.js → desktop-pet-settings-CYyjcSn6.js} +1 -1
  30. package/dist/gateway/static/root/assets/device-pairing-wizard-BPMGvAKE.js +1 -0
  31. package/dist/gateway/static/root/assets/{extension-debug-page-qRpb2FFa.js → extension-debug-page-H_aPMGXW.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-page-C7gs9wu4.js → extension-page-C1JnpeMt.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-settings-page-CyJP1CMZ.js → extension-settings-page-C75kCOqe.js} +1 -1
  34. package/dist/gateway/static/root/assets/{gateway-config-swr-Cpmyrr6j.js → gateway-config-swr-G2lQt7Ak.js} +1 -1
  35. package/dist/gateway/static/root/assets/gateway-settings-BCF732TU.js +1 -0
  36. package/dist/gateway/static/root/assets/{gateway-startup-retry-DoiPeb65.js → gateway-startup-retry-GP31PUrG.js} +1 -1
  37. package/dist/gateway/static/root/assets/{heartbeat-settings-CV6Dolcn.js → heartbeat-settings-C7fjiowt.js} +1 -1
  38. package/dist/gateway/static/root/assets/home-page-DZXeXdUL.js +1 -0
  39. package/dist/gateway/static/root/assets/{image-generation-api-CqjHWl2T.js → image-generation-api-CKFhfHSw.js} +1 -1
  40. package/dist/gateway/static/root/assets/imports-page-GROumhj8.js +1 -0
  41. package/dist/gateway/static/root/assets/{index-CHgOG1aR.js → index-ChIrvv3t.js} +47 -47
  42. package/dist/gateway/static/root/assets/index-R1VNlHjj.css +2 -0
  43. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-B3XqA41D.js → keyboard-shortcuts-settings-zcI822m_.js} +1 -1
  44. package/dist/gateway/static/root/assets/local-app-workbench-page-CAEKGTAg.js +2 -0
  45. package/dist/gateway/static/root/assets/{local-apps-page-BcIVYp0J.js → local-apps-page-BtgDJ92Q.js} +1 -1
  46. package/dist/gateway/static/root/assets/{locale-store-1urrBBCQ.js → locale-store-DlOv7896.js} +1 -1
  47. package/dist/gateway/static/root/assets/logs-page-DHZ4BEkG.js +2 -0
  48. package/dist/gateway/static/root/assets/{management-settings-DNFCdgzR.js → management-settings-DasZxCyD.js} +1 -1
  49. package/dist/gateway/static/root/assets/{markdown-view-BZmcFIWa.js → markdown-view-6oYdBahT.js} +1 -1
  50. package/dist/gateway/static/root/assets/{media-plugins-BDxNhoBC.js → media-plugins-CxthbBvk.js} +1 -1
  51. package/dist/gateway/static/root/assets/messages-Ls1Aaiqd.js +3 -0
  52. package/dist/gateway/static/root/assets/note-detail-panel-BcWq3ddw.js +5 -0
  53. package/dist/gateway/static/root/assets/notes-page-DfZ8QWyv.js +8 -0
  54. package/dist/gateway/static/root/assets/{page-tabs-agH3a2PI.js → page-tabs-BG39KidE.js} +1 -1
  55. package/dist/gateway/static/root/assets/{preview-open-alternatives-XEZ7zDOP.js → preview-open-alternatives-Bevpbq9f.js} +1 -1
  56. package/dist/gateway/static/root/assets/proactive-page-D2U_xQaS.js +1 -0
  57. package/dist/gateway/static/root/assets/{product-open-page-lXAbj85Z.js → product-open-page-Bi1seZpM.js} +1 -1
  58. package/dist/gateway/static/root/assets/project-detail-page-CzlGTW4F.js +1 -0
  59. package/dist/gateway/static/root/assets/projects-page-mQsM-TsP.js +1 -0
  60. package/dist/gateway/static/root/assets/{registry-api-CxQstD00.js → registry-api-D0ddwBFj.js} +1 -1
  61. package/dist/gateway/static/root/assets/remote-access-hub-Cb56JcO1.js +1 -0
  62. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DErKZlVJ.js → runtime-tools-settings-panel-Wkru8_My.js} +2 -2
  63. package/dist/gateway/static/root/assets/{schema-form-DhCmJQVJ.js → schema-form-CSMRzj9Y.js} +1 -1
  64. package/dist/gateway/static/root/assets/sessions-page-Hj9uFcIO.js +1 -0
  65. package/dist/gateway/static/root/assets/settings-advanced-gate-CV4-_EVA.js +1 -0
  66. package/dist/gateway/static/root/assets/{settings-form-section-Dg6chotg.js → settings-form-section-wcAdCkK8.js} +1 -1
  67. package/dist/gateway/static/root/assets/{settings-page-LyuN5N3M.js → settings-page-BcQGAnCs.js} +1 -1
  68. package/dist/gateway/static/root/assets/setup-status-panel-DzxmsaQG.js +1 -0
  69. package/dist/gateway/static/root/assets/{share-preview-page-CZvmf4Ob.js → share-preview-page-DQocUl-1.js} +1 -1
  70. package/dist/gateway/static/root/assets/shares-settings-xlJh1ccG.js +1 -0
  71. package/dist/gateway/static/root/assets/skill-reload-api-wSXJRSxX.js +1 -0
  72. package/dist/gateway/static/root/assets/skills-page-AmqtBJo7.js +2 -0
  73. package/dist/gateway/static/root/assets/{skills-page.utils-Eu0WVHTS.js → skills-page.utils-DtlfCgJp.js} +1 -1
  74. package/dist/gateway/static/root/assets/{system-settings-panel-DQKkM7B2.js → system-settings-panel-Ct698Nf-.js} +1 -1
  75. package/dist/gateway/static/root/assets/task-detail-page-sQpwq5-c.js +1 -0
  76. package/dist/gateway/static/root/assets/{theme-store-Dc5s20V3.js → theme-store-ng8KJsAT.js} +1 -1
  77. package/dist/gateway/static/root/assets/{tunnel-api-TKQpPVd6.js → tunnel-api-Boajol0M.js} +1 -1
  78. package/dist/gateway/static/root/assets/{use-autosave-CkVlrFk1.js → use-autosave-BSyxRHt2.js} +1 -1
  79. package/dist/gateway/static/root/assets/user-model-page-DIEHnAER.js +2 -0
  80. package/dist/gateway/static/root/assets/{voice-api-key-field-B12fFN-L.js → voice-api-key-field-SE3OQI1s.js} +1 -1
  81. package/dist/gateway/static/root/assets/{voice-config-api-Mk78BrpP.js → voice-config-api-VNuCmdFe.js} +1 -1
  82. package/dist/gateway/static/root/assets/{work-discovery-overlay-CT9Zy36i.js → work-discovery-overlay-Dn1vy2oq.js} +1 -1
  83. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-BwefQliE.js → workflow-run-setup-panel-BJEfGY-k.js} +2 -2
  84. package/dist/gateway/static/root/assets/workflows-page-DgRNIyr7.js +6 -0
  85. package/dist/gateway/static/root/index.html +10 -10
  86. package/dist/package.js +1 -1
  87. package/dist/packages/gateway-contract/src/proactive.js +13 -4
  88. package/dist/src/agent/coding/repository-instructions.js +1 -1
  89. package/dist/src/agent/coding/workspace-revision.js +1 -1
  90. package/dist/src/agent/commands/command-isolation.js +1 -1
  91. package/dist/src/agent/tools/factory.js +7 -3
  92. package/dist/src/agent/tools/review-workspace.js +1 -1
  93. package/dist/src/browser/providers/browser-ext-install.js +10 -2
  94. package/dist/src/cli/command-loaders.js +1 -0
  95. package/dist/src/cli/command-manifest.js +4 -0
  96. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  97. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  98. package/dist/src/cli/commands/gateway/run-foreground.js +3 -0
  99. package/dist/src/cli/commands/import.d.ts +2 -0
  100. package/dist/src/cli/commands/import.js +78 -0
  101. package/dist/src/discussions/action-tasks.js +1 -1
  102. package/dist/src/discussions/edits.js +1 -1
  103. package/dist/src/discussions/index.js +1 -1
  104. package/dist/src/discussions/organizer.js +1 -1
  105. package/dist/src/discussions/recording.js +1 -1
  106. package/dist/src/discussions/sealer.js +1 -1
  107. package/dist/src/discussions/service.js +2 -2
  108. package/dist/src/endpoint-tools/invocation-service.d.ts +2 -0
  109. package/dist/src/endpoint-tools/invocation-service.js +48 -19
  110. package/dist/src/gateway/heartbeat/check-store.d.ts +18 -0
  111. package/dist/src/gateway/heartbeat/check-store.js +73 -0
  112. package/dist/src/gateway/heartbeat/service.d.ts +12 -3
  113. package/dist/src/gateway/heartbeat/service.js +61 -44
  114. package/dist/src/gateway/hono/routes/discussions.js +2 -2
  115. package/dist/src/gateway/hono/routes/imports.d.ts +3 -0
  116. package/dist/src/gateway/hono/routes/imports.js +56 -0
  117. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  118. package/dist/src/gateway/hono/routes/proactive-control.js +2 -1
  119. package/dist/src/gateway/service.d.ts +7 -0
  120. package/dist/src/gateway/service.js +26 -9
  121. package/dist/src/imports/compatibility.d.ts +7 -0
  122. package/dist/src/imports/compatibility.js +174 -0
  123. package/dist/src/imports/contextImport.d.ts +3 -0
  124. package/dist/src/imports/contextImport.js +56 -0
  125. package/dist/src/imports/files.d.ts +13 -0
  126. package/dist/src/imports/files.js +77 -0
  127. package/dist/src/imports/importService.d.ts +50 -0
  128. package/dist/src/imports/importService.js +414 -0
  129. package/dist/src/imports/inventory.d.ts +11 -0
  130. package/dist/src/imports/inventory.js +202 -0
  131. package/dist/src/imports/planner.d.ts +11 -0
  132. package/dist/src/imports/planner.js +98 -0
  133. package/dist/src/imports/productImport.d.ts +10 -0
  134. package/dist/src/imports/productImport.js +241 -0
  135. package/dist/src/imports/runtime.d.ts +5 -0
  136. package/dist/src/imports/runtime.js +61 -0
  137. package/dist/src/imports/scanner.d.ts +14 -0
  138. package/dist/src/imports/scanner.js +159 -0
  139. package/dist/src/imports/secrets.d.ts +2 -0
  140. package/dist/src/imports/secrets.js +9 -0
  141. package/dist/src/imports/sources.d.ts +37 -0
  142. package/dist/src/imports/sources.js +75 -0
  143. package/dist/src/imports/types.d.ts +144 -0
  144. package/dist/src/imports/types.js +11 -0
  145. package/dist/src/infra/background-task.d.ts +2 -0
  146. package/dist/src/infra/background-task.js +25 -0
  147. package/dist/src/infra/process-diagnostics.d.ts +5 -0
  148. package/dist/src/infra/process-diagnostics.js +97 -0
  149. package/dist/src/infra/unhandled-rejections.js +6 -3
  150. package/dist/src/infra/update-restart.js +1 -1
  151. package/dist/src/knowledge/ingestion-service.js +1 -0
  152. package/dist/src/knowledge-memory/repository.js +4 -2
  153. package/dist/src/media-shared/http/ssrf-guard.js +1 -1
  154. package/dist/src/notifications/proactive-policy.js +7 -5
  155. package/dist/src/proactive/execution/authorization.d.ts +2 -0
  156. package/dist/src/proactive/execution/authorization.js +10 -1
  157. package/dist/src/proactive/execution/context.js +1 -0
  158. package/dist/src/proactive/execution/repository.d.ts +2 -0
  159. package/dist/src/proactive/execution/repository.js +19 -2
  160. package/dist/src/proactive/execution/worker.js +14 -4
  161. package/dist/src/proactive/experience.d.ts +4 -1
  162. package/dist/src/proactive/experience.js +3 -0
  163. package/dist/src/proactive/follow-ups.d.ts +5 -0
  164. package/dist/src/proactive/follow-ups.js +18 -7
  165. package/dist/src/proactive/inbox/cards.js +2 -1
  166. package/dist/src/proactive/inbox/delivery.js +6 -7
  167. package/dist/src/proactive/inbox/digest.d.ts +1 -1
  168. package/dist/src/proactive/inbox/digest.js +23 -13
  169. package/dist/src/proactive/inbox/notification-policy.d.ts +1 -1
  170. package/dist/src/proactive/inbox/notification-policy.js +9 -6
  171. package/dist/src/proactive/inbox/repository.js +3 -2
  172. package/dist/src/proactive/inbox/service.d.ts +6 -0
  173. package/dist/src/proactive/inbox/service.js +18 -0
  174. package/dist/src/proactive/inbox/types.d.ts +1 -0
  175. package/dist/src/proactive/policy/presence.d.ts +1 -1
  176. package/dist/src/proactive/policy/presence.js +7 -5
  177. package/dist/src/proactive/policy/service.d.ts +8 -5
  178. package/dist/src/proactive/policy/service.js +12 -12
  179. package/dist/src/proactive/scenarios/control.d.ts +3 -3
  180. package/dist/src/proactive/scenarios/control.js +12 -2
  181. package/dist/src/proactive/source-freshness.d.ts +8 -0
  182. package/dist/src/proactive/source-freshness.js +27 -0
  183. package/dist/src/proactive/status.d.ts +2 -0
  184. package/dist/src/proactive/status.js +21 -0
  185. package/dist/src/proactive/temporal/worker.js +2 -1
  186. package/dist/src/realtime/runtime.js +31 -5
  187. package/dist/src/realtime/writer.js +16 -9
  188. package/dist/src/storage/sqlite/import-repository.d.ts +18 -0
  189. package/dist/src/storage/sqlite/import-repository.js +35 -0
  190. package/dist/src/storage/sqlite/import-selection-repository.d.ts +15 -0
  191. package/dist/src/storage/sqlite/import-selection-repository.js +28 -0
  192. package/dist/src/storage/sqlite/knowledge-repository.d.ts +1 -0
  193. package/dist/src/storage/sqlite/knowledge-repository.js +2 -2
  194. package/dist/src/storage/sqlite/migrations/174_capability_imports.sql +11 -0
  195. package/dist/src/storage/sqlite/migrations/175_import_selections.sql +8 -0
  196. package/dist/src/storage/sqlite/migrations/176_proactive_quality.sql +5 -0
  197. package/dist/src/storage/sqlite/migrations/177_proactive_check_controls.sql +25 -0
  198. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  199. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  200. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  201. package/dist/src/storage/sqlite/schema.js +1 -1
  202. package/dist/src/work-discovery/candidate-discovery.js +1 -1
  203. package/dist/src/work-discovery/probe.js +1 -1
  204. package/package.json +4 -3
  205. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-SElFkQd6.js +0 -1
  206. package/dist/gateway/static/root/assets/agents-DYazUaZ6.js +0 -1
  207. package/dist/gateway/static/root/assets/apps-page-BzrodN8C.js +0 -1
  208. package/dist/gateway/static/root/assets/automations-workspace--A4I0jMu.js +0 -6
  209. package/dist/gateway/static/root/assets/browser-settings-page-DDTXUNWK.js +0 -8
  210. package/dist/gateway/static/root/assets/capabilities-settings-panel-Ll5UeXq5.js +0 -8
  211. package/dist/gateway/static/root/assets/channels-settings-BzL15bkU.js +0 -1
  212. package/dist/gateway/static/root/assets/connectors-page-RPw1AOMb.js +0 -2
  213. package/dist/gateway/static/root/assets/device-pairing-wizard-D0_tCX49.js +0 -1
  214. package/dist/gateway/static/root/assets/gateway-settings-DOeeTdWE.js +0 -1
  215. package/dist/gateway/static/root/assets/home-page-Djw1H_Go.js +0 -1
  216. package/dist/gateway/static/root/assets/index-B_Sg5BKz.css +0 -2
  217. package/dist/gateway/static/root/assets/local-app-workbench-page-BPqgyYJq.js +0 -2
  218. package/dist/gateway/static/root/assets/logs-page-BHax3XlS.js +0 -2
  219. package/dist/gateway/static/root/assets/messages-DYFcjtmG.js +0 -3
  220. package/dist/gateway/static/root/assets/note-detail-panel-Cs46LFXO.js +0 -5
  221. package/dist/gateway/static/root/assets/notes-page-BUkcn_0t.js +0 -8
  222. package/dist/gateway/static/root/assets/proactive-page-Bn484J9v.js +0 -1
  223. package/dist/gateway/static/root/assets/project-detail-page-LcH21ixD.js +0 -1
  224. package/dist/gateway/static/root/assets/projects-page-DelBiwyB.js +0 -1
  225. package/dist/gateway/static/root/assets/remote-access-hub-3BjFYXCf.js +0 -1
  226. package/dist/gateway/static/root/assets/sessions-page-CVFirgVd.js +0 -1
  227. package/dist/gateway/static/root/assets/settings-advanced-gate-DyfBpqkY.js +0 -1
  228. package/dist/gateway/static/root/assets/setup-status-panel-CrXhTzHr.js +0 -1
  229. package/dist/gateway/static/root/assets/shares-settings-DyM_D8hM.js +0 -1
  230. package/dist/gateway/static/root/assets/skill-reload-api-Bys58-15.js +0 -1
  231. package/dist/gateway/static/root/assets/skills-page-sWNvFo6D.js +0 -2
  232. package/dist/gateway/static/root/assets/task-detail-page-B18NcZQJ.js +0 -1
  233. package/dist/gateway/static/root/assets/user-model-page-ul-zr58b.js +0 -2
  234. package/dist/gateway/static/root/assets/workflows-page-BY_AWs7S.js +0 -6
@@ -1,6 +1,8 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
3
  import { shouldSilence, stripHeartbeatToken } from "../../heartbeat/tokens.js";
4
+ import { proactiveChecksAllowed, proactivePreferences } from "../../proactive/policy/service.js";
5
+ import { beginHeartbeatCheck, completeHeartbeatCheck, deliverHeartbeatChecks, recentHeartbeatChecks, recoverHeartbeatChecks } from "./check-store.js";
4
6
  import { appendCronEventLines } from "../../heartbeat/event-prompt.js";
5
7
  import { isWithinActiveHours } from "../../heartbeat/active-hours.js";
6
8
  import { isHeartbeatContentEmpty } from "../../heartbeat/content-check.js";
@@ -31,22 +33,41 @@ function heartbeatRunnerConfigFromConfig(cfg) {
31
33
  var HeartbeatService = class {
32
34
  intervalId = null;
33
35
  wake;
34
- lastHeartbeatText = "";
35
- lastHeartbeatAt = 0;
36
+ nextCheckAt = null;
37
+ deliveryTimer = null;
38
+ delivering = false;
39
+ recovered = false;
36
40
  runnerConfig = null;
37
41
  constructor(deps) {
38
42
  this.deps = deps;
39
- this.wake = createHeartbeatWake((reasons) => this.runHeartbeatOnce(reasons));
43
+ this.wake = createHeartbeatWake(async (reasons) => {
44
+ try {
45
+ await this.runHeartbeatOnce(reasons);
46
+ } catch (err) {
47
+ log.error({ err }, "Heartbeat check failed unexpectedly");
48
+ }
49
+ });
40
50
  }
41
51
  start(config) {
52
+ if (this.runnerConfig) this.stop();
42
53
  if (!config.enabled) {
43
54
  log.info("Heartbeat disabled");
44
55
  this.runnerConfig = null;
45
56
  return;
46
57
  }
47
58
  this.runnerConfig = config;
59
+ if (!this.recovered) {
60
+ recoverHeartbeatChecks(this.deps.getWorkspace());
61
+ this.recovered = true;
62
+ }
63
+ this.deliveryTimer = setInterval(() => {
64
+ this.drainDeliveries().catch((err) => log.warn({ err }, "Heartbeat delivery check failed"));
65
+ }, 1e4);
66
+ this.deliveryTimer.unref?.();
67
+ this.nextCheckAt = new Date(Date.now() + config.intervalMs).toISOString();
48
68
  log.info({ intervalMs: config.intervalMs }, "Heartbeat timer started (interval wake)");
49
69
  this.intervalId = setInterval(() => {
70
+ this.nextCheckAt = new Date(Date.now() + config.intervalMs).toISOString();
50
71
  this.wake.request({ reason: "interval" });
51
72
  }, config.intervalMs);
52
73
  this.intervalId.unref?.();
@@ -56,6 +77,9 @@ var HeartbeatService = class {
56
77
  this.wake.request({ reason: opts?.reason ?? "manual" });
57
78
  }
58
79
  stop() {
80
+ if (this.deliveryTimer) clearInterval(this.deliveryTimer);
81
+ this.deliveryTimer = null;
82
+ this.nextCheckAt = null;
59
83
  if (this.intervalId) {
60
84
  clearInterval(this.intervalId);
61
85
  this.intervalId = null;
@@ -73,6 +97,24 @@ var HeartbeatService = class {
73
97
  isRunning() {
74
98
  return this.intervalId !== null;
75
99
  }
100
+ status() {
101
+ const preferences = proactivePreferences(this.deps.getWorkspace());
102
+ return {
103
+ enabled: Boolean(this.runnerConfig?.enabled),
104
+ checksAllowed: proactiveChecksAllowed(preferences),
105
+ nextCheckAt: proactiveChecksAllowed(preferences) ? this.nextCheckAt : null,
106
+ recent: recentHeartbeatChecks(this.deps.getWorkspace())
107
+ };
108
+ }
109
+ async drainDeliveries() {
110
+ if (this.delivering || !this.runnerConfig?.enabled) return;
111
+ this.delivering = true;
112
+ try {
113
+ await deliverHeartbeatChecks(this.deps.getWorkspace(), this.deps.messageBus, this.runnerConfig.target?.trim(), this.runnerConfig.targetChatId?.trim());
114
+ } finally {
115
+ this.delivering = false;
116
+ }
117
+ }
76
118
  async runHeartbeatOnce(reasons) {
77
119
  const reasonSummary = [...new Set(reasons)].join(", ") || "unknown";
78
120
  const cfg = this.runnerConfig;
@@ -80,36 +122,34 @@ var HeartbeatService = class {
80
122
  log.debug({ reasons: reasonSummary }, "Heartbeat: skip (disabled)");
81
123
  return;
82
124
  }
125
+ if (!proactiveChecksAllowed(proactivePreferences(this.deps.getWorkspace()))) return;
83
126
  if (cfg.activeHours && !isWithinActiveHours(cfg.activeHours)) {
84
127
  log.debug({ reasons: reasonSummary }, "Heartbeat: skip (outside active hours)");
85
128
  return;
86
129
  }
130
+ const checkId = beginHeartbeatCheck(this.deps.getWorkspace());
87
131
  const heartbeatPath = resolveHeartbeatMdPath(this.deps.getConfig());
88
132
  if (!heartbeatPath) {
89
- log.debug({ reasons: reasonSummary }, "Heartbeat: skip (no HEARTBEAT path)");
133
+ completeHeartbeatCheck(checkId, "blocked", "No workspace checklist path");
90
134
  return;
91
135
  }
92
136
  let heartbeatContent;
93
137
  try {
94
138
  const raw = await readFile(heartbeatPath, "utf-8");
95
139
  if (isHeartbeatContentEmpty(raw)) {
96
- log.debug({
97
- path: heartbeatPath,
98
- reasons: reasonSummary
99
- }, "Heartbeat: skip (HEARTBEAT.md empty)");
140
+ completeHeartbeatCheck(checkId, "empty_checklist");
100
141
  return;
101
142
  }
102
143
  heartbeatContent = raw.trim();
103
144
  } catch {
104
- log.debug({
105
- path: heartbeatPath,
106
- reasons: reasonSummary
107
- }, "Heartbeat: HEARTBEAT.md missing; continuing");
145
+ completeHeartbeatCheck(checkId, "blocked", "Checklist could not be read");
146
+ return;
108
147
  }
109
148
  const sessionKey = cfg.isolatedSession ? `heartbeat:isolated:${Date.now()}` : "heartbeat:main";
110
149
  let basePrompt = (cfg.prompt?.trim() || DEFAULT_PROMPT).trim();
111
150
  if (heartbeatContent) basePrompt = `${basePrompt}\n\n---\nHEARTBEAT.md:\n${heartbeatContent}\n---`;
112
151
  basePrompt = appendCronEventLines(basePrompt, reasons);
152
+ basePrompt += "\nReturn notifications in your final response. Do not send messages, media or voice directly; delivery follows the user notification policy.";
113
153
  const prompt = `${basePrompt}\n\nCurrent time: ${(/* @__PURE__ */ new Date()).toISOString()}`;
114
154
  const ackMax = cfg.ackMaxChars ?? 300;
115
155
  log.debug({
@@ -143,14 +183,21 @@ var HeartbeatService = class {
143
183
  source: "heartbeat"
144
184
  });
145
185
  } catch (error) {
186
+ completeHeartbeatCheck(checkId, "failed", "Agent check failed; see gateway logs");
146
187
  log.error({ err: error }, "Heartbeat: agent call failed");
147
188
  return;
148
189
  }
190
+ if (cfg !== this.runnerConfig || !proactiveChecksAllowed(proactivePreferences(this.deps.getWorkspace()))) {
191
+ completeHeartbeatCheck(checkId, "cancelled", "Checks paused or configuration changed");
192
+ return;
193
+ }
149
194
  if (!reply?.trim()) {
195
+ completeHeartbeatCheck(checkId, "no_change");
150
196
  log.debug({ reasons: reasonSummary }, "Heartbeat: skip (empty model reply)");
151
197
  return;
152
198
  }
153
199
  if (shouldSilence(reply, ackMax)) {
200
+ completeHeartbeatCheck(checkId, "no_change");
154
201
  log.info({
155
202
  ackMax,
156
203
  replyChars: reply.length,
@@ -159,38 +206,8 @@ var HeartbeatService = class {
159
206
  return;
160
207
  }
161
208
  const { stripped } = stripHeartbeatToken(reply);
162
- const finalText = stripped || reply.trim();
163
- if (this.isDuplicate(finalText)) {
164
- log.info({
165
- finalTextChars: finalText.length,
166
- reasons: reasonSummary
167
- }, "Heartbeat: not sent — duplicate within 24h");
168
- return;
169
- }
170
- const target = cfg.target?.trim();
171
- const targetChatId = cfg.targetChatId?.trim();
172
- if (Boolean(target && targetChatId)) {
173
- await this.deps.messageBus.publishOutbound({
174
- channel: target,
175
- chat_id: targetChatId,
176
- content: finalText,
177
- type: "message"
178
- });
179
- log.info({
180
- reasons: reasonSummary,
181
- channel: target,
182
- chatId: targetChatId,
183
- contentChars: finalText.length
184
- }, "Heartbeat: sent — outbound queued");
185
- } else log.info({
186
- reasons: reasonSummary,
187
- finalTextChars: finalText.length
188
- }, "Heartbeat: not sent — no delivery target (set gateway.heartbeat.target + targetChatId)");
189
- this.lastHeartbeatText = finalText;
190
- this.lastHeartbeatAt = Date.now();
191
- }
192
- isDuplicate(text) {
193
- return text.trim() === this.lastHeartbeatText.trim() && Date.now() - this.lastHeartbeatAt < 1440 * 60 * 1e3;
209
+ completeHeartbeatCheck(checkId, "prepared", void 0, stripped || reply.trim(), cfg.target?.trim(), cfg.targetChatId?.trim());
210
+ await this.drainDeliveries();
194
211
  }
195
212
  };
196
213
  //#endregion
@@ -1,11 +1,11 @@
1
1
  import { DISCUSSION_STATUSES } from "../../../../packages/gateway-contract/src/discussions.js";
2
2
  import { hasGatewayScope } from "../../security/gateway-scopes.js";
3
3
  import { DiscussionServiceError } from "../../../discussions/errors.js";
4
- import { convertMeetingAction, meetingActionTasks } from "../../../discussions/action-tasks.js";
5
4
  import { MeetingEditSchema } from "../../../discussions/edits.js";
6
- import { exportDiscussion } from "../../../discussions/export.js";
7
5
  import "../../../discussions/index.js";
8
6
  import { getGatewayPrincipal } from "../../security/gateway-principal.js";
7
+ import { convertMeetingAction, meetingActionTasks } from "../../../discussions/action-tasks.js";
8
+ import { exportDiscussion } from "../../../discussions/export.js";
9
9
  import { createReadStream } from "node:fs";
10
10
  import { stat } from "node:fs/promises";
11
11
  import { Readable } from "node:stream";
@@ -0,0 +1,3 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AuthenticatedRouteDeps } from './deps.js';
3
+ export declare function registerImportRoutes(app: Hono, deps: AuthenticatedRouteDeps): void;
@@ -0,0 +1,56 @@
1
+ import { getGatewayPrincipal } from "../../security/gateway-principal.js";
2
+ import { ImportError } from "../../../imports/types.js";
3
+ import { isImportSource } from "../../../imports/sources.js";
4
+ import { ImportSelectionRepository } from "../../../storage/sqlite/import-selection-repository.js";
5
+ import { getLiveInventory, previewInventoryItem, publicInventory, scanProduct } from "../../../imports/inventory.js";
6
+ import { importSelection, listImportSources } from "../../../imports/productImport.js";
7
+ import { createRuntimeImportService } from "../../../imports/runtime.js";
8
+ import { z } from "zod";
9
+ //#region src/gateway/hono/routes/imports.ts
10
+ const selectionSchema = z.object({
11
+ inventoryId: z.string().uuid(),
12
+ candidateIds: z.array(z.string().uuid()).min(1).max(5e3),
13
+ requestId: z.string().uuid(),
14
+ retryOf: z.string().uuid().optional()
15
+ }).strict();
16
+ function registerImportRoutes(app, deps) {
17
+ const service = () => createRuntimeImportService("gateway-owner", () => deps.service.agentService.refreshSkillsAfterDiskChange(), (root) => deps.service.agentService.getWorkspaceTrust(root).trusted);
18
+ const respond = async (c, run) => {
19
+ try {
20
+ if (getGatewayPrincipal(c).kind !== "owner") throw new ImportError("owner_required", "Only the Gateway owner can import local capabilities", 403);
21
+ return c.json({
22
+ ok: true,
23
+ data: await run()
24
+ });
25
+ } catch (error) {
26
+ if (error instanceof z.ZodError) return c.json({
27
+ ok: false,
28
+ code: "invalid_input",
29
+ error: "Invalid import request"
30
+ }, 400);
31
+ if (error instanceof ImportError) return c.json({
32
+ ok: false,
33
+ code: error.code,
34
+ error: error.message
35
+ }, error.status);
36
+ return c.json({
37
+ ok: false,
38
+ code: "import_failed",
39
+ error: "Import could not finish. Existing content is preserved."
40
+ }, 400);
41
+ }
42
+ };
43
+ app.get("/api/imports/sources", (c) => respond(c, () => ({ sources: listImportSources("gateway-owner") })));
44
+ app.post("/api/imports/sources/:source/scan", deps.strictRateLimitMiddleware, (c) => respond(c, async () => {
45
+ const source = c.req.param("source");
46
+ if (!isImportSource(source)) throw new ImportError("unsupported_source", "Unsupported source");
47
+ z.object({}).strict().parse(await c.req.json());
48
+ return scanProduct(service(), source);
49
+ }));
50
+ app.get("/api/imports/inventories/:id", (c) => respond(c, () => publicInventory(getLiveInventory("gateway-owner", c.req.param("id")))));
51
+ app.get("/api/imports/inventories/:id/items/:itemId/preview", (c) => respond(c, () => previewInventoryItem(service(), "gateway-owner", c.req.param("id"), c.req.param("itemId"))));
52
+ app.post("/api/imports/runs", deps.strictRateLimitMiddleware, (c) => respond(c, async () => importSelection(service(), selectionSchema.parse(await c.req.json()))));
53
+ app.get("/api/imports/runs/:id", (c) => respond(c, () => new ImportSelectionRepository("gateway-owner").get("run", c.req.param("id"))));
54
+ }
55
+ //#endregion
56
+ export { registerImportRoutes };
@@ -3,6 +3,14 @@ function startsWithAny(path, prefixes) {
3
3
  return prefixes.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));
4
4
  }
5
5
  const AUTHENTICATED_LAZY_ROUTE_BUNDLES = [
6
+ {
7
+ id: "imports",
8
+ match: (path) => startsWithAny(path, ["/api/imports"]),
9
+ load: async () => {
10
+ const { registerImportRoutes } = await import("./imports.js");
11
+ return { register: registerImportRoutes };
12
+ }
13
+ },
6
14
  {
7
15
  id: "project-understanding",
8
16
  match: (path) => /^\/api\/projects\/[^/]+\/understanding$/.test(path),
@@ -33,7 +33,8 @@ function registerProactiveControlRoutes(app, deps) {
33
33
  }));
34
34
  app.get("/api/proactive/overview", (c) => c.json({
35
35
  ok: true,
36
- ...delegationOverview(workspace())
36
+ ...delegationOverview(workspace()),
37
+ heartbeat: deps.service.heartbeatStatus()
37
38
  }));
38
39
  app.post("/api/proactive/delegations", deps.strictRateLimitMiddleware, async (c) => c.json({
39
40
  ok: true,
@@ -124,6 +124,7 @@ export declare class GatewayService {
124
124
  private ensureHeartbeatService;
125
125
  private createTaskRunDispatcher;
126
126
  private createNotificationService;
127
+ private readonly taskRunDispatch;
127
128
  dispatchTaskRuns(): void;
128
129
  ensureTaskConversation(taskId: string, options?: {
129
130
  runId?: string;
@@ -255,6 +256,12 @@ export declare class GatewayService {
255
256
  manageable: boolean;
256
257
  order: number;
257
258
  }>;
259
+ heartbeatStatus(): {
260
+ enabled: boolean;
261
+ checksAllowed: boolean;
262
+ nextCheckAt: string;
263
+ recent: import("./heartbeat/check-store.js").HeartbeatCheck[];
264
+ };
258
265
  /**
259
266
  * Request an immediate heartbeat run (coalesced like interval/cron wakes).
260
267
  */
@@ -63,23 +63,24 @@ import { ConnectionRecoveryService } from "../connectors/connection-recovery-ser
63
63
  import { startConnectorLearningCoordinator } from "../connectors/learning-coordinator.js";
64
64
  import { ConnectedSourceChangePublisher } from "../connectors/source-change-publisher.js";
65
65
  import { startConnectorSupervisor } from "../connectors/supervisor.js";
66
- import { DiscussionService } from "../discussions/service.js";
67
- import { DiscussionOrganizer } from "../discussions/organizer.js";
68
- import { DiscussionLiveWorker } from "../discussions/live-worker.js";
69
- import { DiscussionOrganizerWorker } from "../discussions/organizer-worker.js";
70
- import { DiscussionSealer } from "../discussions/sealer.js";
71
- import "../discussions/index.js";
72
66
  import { EndpointToolRuntime } from "../endpoint-tools/runtime.js";
73
67
  import "../endpoint-tools/index.js";
74
68
  import { assertGatewayAuthConfigured, extractToken, resolveGatewayAuth, validateToken } from "./auth.js";
75
69
  import { prepareAutomationAgentSession } from "./automation-agent-session.js";
76
70
  import { stopTailscaleExposure } from "./tailscale-lifecycle.js";
71
+ import { createBackgroundTask } from "../infra/background-task.js";
77
72
  import { buildTaskAgentContext } from "../agent/source-context/task-context.js";
78
73
  import { deliverProactiveCard } from "../proactive/inbox/delivery.js";
79
74
  import { WorkDiscoveryService } from "../work-discovery/service.js";
80
75
  import { buildVoiceMemoryContext } from "../voice/realtime/memory-context.js";
81
76
  import { buildVoicePersonaContext } from "../voice/realtime/persona-context.js";
82
77
  import { voiceMemoryBudget, voicePersonaBudget } from "../voice/realtime/conversation-context.js";
78
+ import { DiscussionService } from "../discussions/service.js";
79
+ import { DiscussionOrganizer } from "../discussions/organizer.js";
80
+ import { DiscussionLiveWorker } from "../discussions/live-worker.js";
81
+ import { DiscussionOrganizerWorker } from "../discussions/organizer-worker.js";
82
+ import { DiscussionSealer } from "../discussions/sealer.js";
83
+ import "../discussions/index.js";
83
84
  import { NotesStore } from "../notes/store.js";
84
85
  import { buildNoteAgentContext } from "../notes/agent-context.js";
85
86
  import { NotesService } from "../notes/service.js";
@@ -665,7 +666,8 @@ var GatewayService = class {
665
666
  agentService: this.ensureAgentService(),
666
667
  messageBus: this.bus,
667
668
  sessionStore: this.sessionIndex.getStore(),
668
- getConfig: () => this.config
669
+ getConfig: () => this.config,
670
+ getWorkspace: () => this.workspacePath
669
671
  });
670
672
  return this.heartbeatService;
671
673
  }
@@ -721,11 +723,21 @@ var GatewayService = class {
721
723
  });
722
724
  return this.notificationService;
723
725
  }
724
- dispatchTaskRuns() {
726
+ taskRunDispatch = createBackgroundTask(async () => {
725
727
  new TaskSignalService().tick();
726
728
  this.dispatchTaskEvents();
727
729
  this.createTaskRunDispatcher().dispatch();
728
- this.createWorkflowRunService().dispatchTaskRuns();
730
+ await this.createWorkflowRunService().dispatchTaskRuns();
731
+ }, (err) => {
732
+ const errorMessage = err instanceof Error ? err.message : String(err);
733
+ log.error({
734
+ err,
735
+ errorMessage,
736
+ phase: "task_run_dispatch"
737
+ }, `Task dispatch failed: ${errorMessage}`);
738
+ });
739
+ dispatchTaskRuns() {
740
+ this.taskRunDispatch();
729
741
  }
730
742
  async ensureTaskConversation(taskId, options = {}) {
731
743
  const task = new TaskRepository().require(taskId);
@@ -955,6 +967,8 @@ var GatewayService = class {
955
967
  });
956
968
  log.debug("Starting gateway service...");
957
969
  openXopcDatabase();
970
+ const { recoverCapabilityImports } = await import("../imports/runtime.js");
971
+ await recoverCapabilityImports();
958
972
  this.createNotificationService().start();
959
973
  this.startTime = Date.now();
960
974
  this.running = true;
@@ -1429,6 +1443,9 @@ var GatewayService = class {
1429
1443
  return a.id.localeCompare(b.id);
1430
1444
  });
1431
1445
  }
1446
+ heartbeatStatus() {
1447
+ return this.heartbeatService?.status() ?? null;
1448
+ }
1432
1449
  /**
1433
1450
  * Request an immediate heartbeat run (coalesced like interval/cron wakes).
1434
1451
  */
@@ -0,0 +1,7 @@
1
+ import type { ImportCandidate, ImportFile } from './types.js';
2
+ export declare function parseSkill(raw: string): {
3
+ metadata: Record<string, unknown>;
4
+ body: string;
5
+ };
6
+ export declare function inspectSkill(base: Pick<ImportCandidate, 'id' | 'source' | 'scope' | 'location' | 'shared'>, files: ImportFile[]): ImportCandidate;
7
+ export declare function inspectMcp(base: Pick<ImportCandidate, 'id' | 'source' | 'scope' | 'location' | 'shared'>, name: string, raw: unknown): ImportCandidate;
@@ -0,0 +1,174 @@
1
+ import { McpServerSchema, init_schema } from "../config/schema.js";
2
+ import { isValidSkillId } from "../agent/skills/managed-store.js";
3
+ import { containsSecret, isCredentialPath } from "./secrets.js";
4
+ import { digest, fileHash } from "./files.js";
5
+ import yaml from "js-yaml";
6
+ //#region src/imports/compatibility.ts
7
+ init_schema();
8
+ function parseSkill(raw) {
9
+ const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)([\s\S]*)$/.exec(raw);
10
+ if (!match || match[1].length > 64 * 1024) throw new Error("A valid YAML header is required");
11
+ if (/(?:^|\s)[&*][\w-]+/.test(match[1])) throw new Error("YAML aliases are not supported");
12
+ let metadata;
13
+ try {
14
+ metadata = yaml.load(match[1], { schema: yaml.JSON_SCHEMA });
15
+ } catch {
16
+ throw new Error("Invalid YAML header");
17
+ }
18
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) throw new Error("Invalid skill metadata");
19
+ return {
20
+ metadata,
21
+ body: match[2]
22
+ };
23
+ }
24
+ function inspectSkill(base, files) {
25
+ const item = {
26
+ ...base,
27
+ kind: "skill",
28
+ name: base.id,
29
+ description: "",
30
+ files,
31
+ hash: fileHash(files),
32
+ compatibility: "compatible",
33
+ findings: [],
34
+ requiredEnv: []
35
+ };
36
+ try {
37
+ if (files.reduce((n, f) => n + Buffer.byteLength(f.data, "base64"), 0) > 15 * 1024 * 1024) throw new Error("Skill exceeds 15 MiB");
38
+ if (files.some((f) => f.path.endsWith("/SKILL.md"))) throw new Error("Nested skill scopes need separate imports");
39
+ const primary = files.find((f) => f.path === "SKILL.md");
40
+ if (!primary || Buffer.byteLength(primary.data, "base64") > 1024 * 1024) throw new Error("SKILL.md is missing or exceeds 1 MiB");
41
+ const { metadata, body } = parseSkill(Buffer.from(primary.data, "base64").toString("utf8"));
42
+ if (typeof metadata.name !== "string" || !isValidSkillId(metadata.name)) throw new Error("Skill name must be a portable identifier");
43
+ item.name = metadata.name;
44
+ if (typeof metadata.description !== "string" || !metadata.description.trim() || metadata.description.length > 1024) throw new Error("Skill description must contain 1–1024 characters");
45
+ item.description = metadata.description;
46
+ const supported = new Set([
47
+ "name",
48
+ "description",
49
+ "license",
50
+ "compatibility",
51
+ "metadata",
52
+ "disable-model-invocation",
53
+ "allowed-tools"
54
+ ]);
55
+ for (const key of Object.keys(metadata)) if (!supported.has(key)) item.findings.push(`Unsupported skill field: ${key}`);
56
+ if (metadata["disable-model-invocation"] !== void 0 && typeof metadata["disable-model-invocation"] !== "boolean") item.findings.push("disable-model-invocation must be boolean");
57
+ if (/\$ARGUMENTS|\$\{?(?:CLAUDE_|CODEX_)|!`/.test(body)) item.findings.push("Product-specific variables or command expansion require adaptation");
58
+ const meta = metadata.metadata;
59
+ if (meta && (typeof meta !== "object" || Array.isArray(meta))) item.findings.push("Invalid metadata");
60
+ if (meta && Object.keys(meta).some((k) => ![
61
+ "author",
62
+ "version",
63
+ "short-description",
64
+ "short_description",
65
+ "category",
66
+ "tags",
67
+ "license",
68
+ "homepage"
69
+ ].includes(k))) item.findings.push("Extended metadata needs manual compatibility review");
70
+ for (const match of body.matchAll(/\[[^\]]*\]\(([^)\s]+)\)/g)) {
71
+ const ref = match[1].split("#")[0];
72
+ if (ref && !/^(?:[a-z]+:|#)/i.test(ref) && !files.some((f) => f.path === ref.replace(/^\.\//, ""))) item.findings.push("A referenced local file is missing or outside the skill");
73
+ }
74
+ if (item.findings.length) item.compatibility = "blocked";
75
+ if (metadata["allowed-tools"]) item.findings.push("Source tool pre-approvals are not transferred; xopc permissions apply");
76
+ for (const file of files) if (isCredentialPath(file.path) || containsSecret(Buffer.from(file.data, "base64").toString("utf8"))) {
77
+ item.compatibility = "blocked";
78
+ item.findings.push("Possible credentials found; remove them from the source and scan again");
79
+ item.files = [];
80
+ item.description = "";
81
+ break;
82
+ }
83
+ } catch (error) {
84
+ item.compatibility = "blocked";
85
+ item.findings.push(error instanceof Error ? error.message : "Invalid skill");
86
+ }
87
+ if (files.some((f) => containsSecret(Buffer.from(f.data, "base64").toString("utf8")))) {
88
+ item.files = [];
89
+ item.description = "";
90
+ item.compatibility = "blocked";
91
+ item.findings = ["Possible credentials found; remove them and scan again"];
92
+ }
93
+ return item;
94
+ }
95
+ function inspectMcp(base, name, raw) {
96
+ const item = {
97
+ ...base,
98
+ kind: "mcp",
99
+ name,
100
+ description: "MCP connection draft",
101
+ files: [],
102
+ hash: "",
103
+ compatibility: "needs_setup",
104
+ findings: [],
105
+ requiredEnv: []
106
+ };
107
+ const value = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
108
+ const draft = {};
109
+ const supported = new Set([
110
+ "command",
111
+ "args",
112
+ "cwd",
113
+ "url",
114
+ "type",
115
+ "env",
116
+ "headers",
117
+ "http_headers",
118
+ "startup_timeout_sec",
119
+ "tool_timeout_sec"
120
+ ]);
121
+ for (const [key, v] of Object.entries(value)) {
122
+ if (!supported.has(key)) {
123
+ item.compatibility = "blocked";
124
+ item.findings.push(`Unsupported MCP field: ${key}`);
125
+ continue;
126
+ }
127
+ if ([
128
+ "env",
129
+ "headers",
130
+ "http_headers"
131
+ ].includes(key)) {
132
+ if (v && typeof v === "object") item.requiredEnv.push(...Object.keys(v));
133
+ item.findings.push(`${key} values omitted; configure them in xopc`);
134
+ } else if (key === "type") {
135
+ if (v === "http") draft.transport = "streamable-http";
136
+ else if (v === "sse") draft.transport = "sse";
137
+ else if (v !== "stdio") {
138
+ item.compatibility = "blocked";
139
+ item.findings.push("Unsupported MCP transport");
140
+ }
141
+ } else if (key === "startup_timeout_sec" || key === "tool_timeout_sec") if (typeof v === "number") draft[key === "startup_timeout_sec" ? "connectionTimeoutMs" : "requestTimeoutMs"] = v * 1e3;
142
+ else {
143
+ item.compatibility = "blocked";
144
+ item.findings.push("Invalid MCP timeout");
145
+ }
146
+ else draft[key] = v;
147
+ }
148
+ if (typeof draft.url === "string") try {
149
+ const url = new URL(draft.url);
150
+ if (url.username || url.password || url.search || url.hash) {
151
+ delete draft.url;
152
+ item.findings.push("URL credentials or query parameters omitted; enter a clean endpoint");
153
+ }
154
+ } catch {
155
+ delete draft.url;
156
+ }
157
+ if (containsSecret(JSON.stringify(draft)) || /--?[a-z-]*(?:token|password|secret|api-key|authorization)(?:[=\s\"]|$)/i.test(JSON.stringify(draft))) {
158
+ Object.keys(draft).forEach((k) => delete draft[k]);
159
+ item.findings.push("Possible credentials omitted from connection fields");
160
+ }
161
+ if (!McpServerSchema.safeParse(draft).success || !draft.command && !draft.url) {
162
+ item.compatibility = "blocked";
163
+ item.findings.push("A valid command or URL is required");
164
+ }
165
+ if (base.scope === "project") {
166
+ item.compatibility = "blocked";
167
+ item.findings.push("Project-scoped MCP activation is not supported");
168
+ }
169
+ item.mcp = draft;
170
+ item.hash = digest(draft);
171
+ return item;
172
+ }
173
+ //#endregion
174
+ export { inspectMcp, inspectSkill, parseSkill };
@@ -0,0 +1,3 @@
1
+ import type { ImportCandidate } from './types.js';
2
+ /** Small source records fit the existing retrieval budget without injecting entire documents. */
3
+ export declare function importContextDocument(item: ImportCandidate, projectId?: string): 'created' | 'unchanged' | 'kept';
@@ -0,0 +1,56 @@
1
+ import { runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
2
+ import { listKnowledgeItems, writeKnowledgeItem } from "../knowledge-memory/repository.js";
3
+ import "../knowledge-memory/index.js";
4
+ import { digest } from "./files.js";
5
+ //#region src/imports/contextImport.ts
6
+ /** Small source records fit the existing retrieval budget without injecting entire documents. */
7
+ function importContextDocument(item, projectId) {
8
+ const content = item.content?.trim();
9
+ if (!content) throw new Error("Missing context");
10
+ const scope = projectId ? {
11
+ type: "project",
12
+ id: projectId
13
+ } : { type: "global" };
14
+ const canonicalKey = `product-import:${item.source}:${digest(item.location)}`;
15
+ return runSqliteWriteTransaction(() => {
16
+ const existing = listKnowledgeItems({
17
+ canonicalKey,
18
+ scope,
19
+ statuses: [
20
+ "active",
21
+ "candidate",
22
+ "needs_review",
23
+ "stale",
24
+ "archived",
25
+ "rejected"
26
+ ]
27
+ })[0];
28
+ if (existing) return existing.source.hash === item.hash ? "unchanged" : "kept";
29
+ for (let offset = 0, part = 0; offset < content.length; offset += 2e3, part++) {
30
+ const chunk = content.slice(offset, offset + 2e3).trim();
31
+ if (!chunk) continue;
32
+ writeKnowledgeItem({
33
+ kind: "note",
34
+ scope,
35
+ content: chunk,
36
+ canonicalKey: part === 0 ? canonicalKey : `${canonicalKey}:part:${part}`,
37
+ recordClass: "source_index",
38
+ status: "active",
39
+ confidence: .7,
40
+ importance: .7,
41
+ originClass: "system",
42
+ source: {
43
+ kind: "product_import",
44
+ product: item.source,
45
+ path: item.location,
46
+ hash: item.hash,
47
+ part,
48
+ importedAt: Date.now()
49
+ }
50
+ });
51
+ }
52
+ return "created";
53
+ });
54
+ }
55
+ //#endregion
56
+ export { importContextDocument };
@@ -0,0 +1,13 @@
1
+ import { type ImportFile } from './types.js';
2
+ export declare const IMPORT_LIMITS: {
3
+ file: number;
4
+ total: number;
5
+ count: number;
6
+ depth: number;
7
+ };
8
+ export declare function digest(value: unknown): string;
9
+ export declare function fileHash(files: ImportFile[]): string;
10
+ export declare function inside(root: string, path: string): boolean;
11
+ export declare function safePath(path: string): boolean;
12
+ export declare function readRegularFile(path: string, limit?: number): Buffer;
13
+ export declare function readTree(rootPath: string): ImportFile[];