limixbot 0.3.24 → 0.3.25

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 (137) hide show
  1. package/dist/assets/{ActivityPanel-BjjmX49M.js → ActivityPanel-CgPkGKZZ.js} +1 -1
  2. package/dist/assets/{App-BWtQhGWp.js → App-lurtH-qI.js} +9 -9
  3. package/dist/assets/{AppEventEditor-Bq1-MGxi.js → AppEventEditor-G_71EWui.js} +1 -1
  4. package/dist/assets/BotSettingsDialog-BUvuLu53.js +16 -0
  5. package/dist/assets/{BrowserPanel-CPLcno_m.js → BrowserPanel-DrKZNyYB.js} +1 -1
  6. package/dist/assets/{BrowserProfilesManager-D9JsCp1k.js → BrowserProfilesManager-CtiUbbdv.js} +1 -1
  7. package/dist/assets/{CloudAddDialog-DcmN7A5a.js → CloudAddDialog-D4BX2927.js} +1 -1
  8. package/dist/assets/{ComputerFilesPane-Bjx6TGmM.js → ComputerFilesPane-RnmlJhlD.js} +1 -1
  9. package/dist/assets/{ComputerPanel-C9Xx4Ifs.js → ComputerPanel-D8upQSmd.js} +2 -2
  10. package/dist/assets/{DataPanel-IfpQCi3b.js → DataPanel-MZcku4SL.js} +1 -1
  11. package/dist/assets/{DesktopViewer-RjjUUlrQ.js → DesktopViewer-p7N7cqle.js} +1 -1
  12. package/dist/assets/{FilesView-D_0GwHAN.js → FilesView-CDbtoPxh.js} +1 -1
  13. package/dist/assets/{GrokMoveDialog-CUie-0en.js → GrokMoveDialog-BebZb43R.js} +1 -1
  14. package/dist/assets/{InspectorPanel-BuFL6wWf.js → InspectorPanel-BMkRIuTz.js} +1 -1
  15. package/dist/assets/{KeyboardShortcutsModal-uOhXagTA.js → KeyboardShortcutsModal-3XyVcNTZ.js} +1 -1
  16. package/dist/assets/{LiveDesktop-BwpZa-Ri.js → LiveDesktop-BkRR5lCn.js} +1 -1
  17. package/dist/assets/{LocalVmWorkspace-CousHKOb.js → LocalVmWorkspace-9l9iWtAy.js} +1 -1
  18. package/dist/assets/{MacLocalControl-BKDzplO2.js → MacLocalControl-B45d0l7H.js} +1 -1
  19. package/dist/assets/{MarkdownFilePreview-CCwc1Tek.js → MarkdownFilePreview-ChR-f9oD.js} +1 -1
  20. package/dist/assets/{MenuMotion-CSgaJLc2.js → MenuMotion-BBaxdyL_.js} +1 -1
  21. package/dist/assets/{NewBotDialog-BtvoCpqN.js → NewBotDialog-C_uQRhxy.js} +1 -1
  22. package/dist/assets/{PhoneAppDialog-C1hIwyT8.js → PhoneAppDialog-CDO1xUXD.js} +1 -1
  23. package/dist/assets/{PlacesRail-PP97GpM6.js → PlacesRail-iu-rjh5s.js} +1 -1
  24. package/dist/assets/{PluginsPanel-DpUEC1zc.js → PluginsPanel-DlQBRYQB.js} +1 -1
  25. package/dist/assets/{ProIntroduction-C-7uexIW.js → ProIntroduction-RbtTSiR6.js} +1 -1
  26. package/dist/assets/{RemoteAgentSettingsPanel-DtofJtgF.js → RemoteAgentSettingsPanel-C8r-80J4.js} +1 -1
  27. package/dist/assets/{RoomPanel-CHbgQ0xd.js → RoomPanel-BIUTnm5N.js} +1 -1
  28. package/dist/assets/{RoutinesPage-ZdFTYQc-.js → RoutinesPage-DROK-336.js} +1 -1
  29. package/dist/assets/{SettingsModal-Cdz3bEYZ.js → SettingsModal--yYGUYK2.js} +3 -3
  30. package/dist/assets/{ShareTeamDialog-CNu6Kwet.js → ShareTeamDialog-CVnQoRVQ.js} +1 -1
  31. package/dist/assets/{SignedInSites-CyWcDbLW.js → SignedInSites-B-iBIlfL.js} +1 -1
  32. package/dist/assets/TeamLibraryPanel-CwVaJVC_.js +1 -0
  33. package/dist/assets/{TeamMapPage-C2PnNdil.js → TeamMapPage-kXk9hCNu.js} +1 -1
  34. package/dist/assets/{TriggersPage-B_br6xBZ.js → TriggersPage-prZBUP3d.js} +1 -1
  35. package/dist/assets/{VisibilitySection-DH0WrBCn.js → VisibilitySection-DJjvTyLx.js} +1 -1
  36. package/dist/assets/{WebhookTrigger-BASBoMb_.js → WebhookTrigger-BAFikH71.js} +1 -1
  37. package/dist/assets/{WorksOnGrid-CboITAAs.js → WorksOnGrid-DJmfLtP8.js} +1 -1
  38. package/dist/assets/{abnfDiagram-YUKMZFIV-CJ69o_vp.js → abnfDiagram-YUKMZFIV-DUI6uevQ.js} +1 -1
  39. package/dist/assets/{activity-BdRjGS2C.js → activity-DVLFekjx.js} +1 -1
  40. package/dist/assets/{architectureDiagram-47P4ROYG-BH57AmKC.js → architectureDiagram-47P4ROYG-CMELW5_4.js} +1 -1
  41. package/dist/assets/{blockDiagram-E7TT5TSB-D3ebMs_o.js → blockDiagram-E7TT5TSB-C3xD0z1e.js} +1 -1
  42. package/dist/assets/{c4Diagram-CRA5TL53-6TrdcGZR.js → c4Diagram-CRA5TL53-Cm9X0GEV.js} +1 -1
  43. package/dist/assets/{calendar-days-DiMBTH2e.js → calendar-days-CDvwJ_d4.js} +1 -1
  44. package/dist/assets/channel-CsOUN1ZX.js +1 -0
  45. package/dist/assets/{chunk-2Q5K7J3B-BemwHdji.js → chunk-2Q5K7J3B-LWcbjcjL.js} +1 -1
  46. package/dist/assets/{chunk-5VM5RSS4-CQZbLBld.js → chunk-5VM5RSS4-B3rNoWeE.js} +1 -1
  47. package/dist/assets/{chunk-7TKQ45FW-Dy6EXFTX.js → chunk-7TKQ45FW-FL9R0rHe.js} +1 -1
  48. package/dist/assets/{chunk-AW2ZBBNX-BbKGGyK8.js → chunk-AW2ZBBNX-JRef_4rd.js} +1 -1
  49. package/dist/assets/{chunk-FVRAUYC3-DHqUalc2.js → chunk-FVRAUYC3-C__loQFR.js} +2 -2
  50. package/dist/assets/{chunk-HTAEGDNF-B66Vjlwq.js → chunk-HTAEGDNF-Bx74_xmN.js} +1 -1
  51. package/dist/assets/{chunk-JWPE2WC7-DrtdT6L3.js → chunk-JWPE2WC7-B20vr3uH.js} +1 -1
  52. package/dist/assets/{chunk-KQW6MTUR-t7RW8fFT.js → chunk-KQW6MTUR-DfO9J2pt.js} +1 -1
  53. package/dist/assets/{chunk-XXDRQBXY-CntyS42N.js → chunk-XXDRQBXY-BAPGqnqj.js} +1 -1
  54. package/dist/assets/{classDiagram-v2-K4WV4PDN-KlNCgeTt.js → classDiagram-v2-K4WV4PDN-C3VR3yN8.js} +1 -1
  55. package/dist/assets/{coins-BHKpT34i.js → coins-CueFc0mY.js} +1 -1
  56. package/dist/assets/{core-DEy6KDLR.js → core-Bw6kaGwe.js} +1 -1
  57. package/dist/assets/{cose-bilkent-JH36ORCC-C_cXoOGT.js → cose-bilkent-JH36ORCC-19p3m04s.js} +1 -1
  58. package/dist/assets/{cynefin-EF2NZ3EQ-DGuwW5zt.js → cynefin-EF2NZ3EQ-Tv0tce7B.js} +1 -1
  59. package/dist/assets/{cynefinDiagram-3GCD6N5R-qMzxXkVk.js → cynefinDiagram-3GCD6N5R-Iin0JwlW.js} +1 -1
  60. package/dist/assets/{dagre-W4DXFKR2-Q-osY3Yi.js → dagre-W4DXFKR2-00TRmSmB.js} +1 -1
  61. package/dist/assets/{diagram-2UJZ2QOL-DyVJSBOF.js → diagram-2UJZ2QOL-DslMBjKt.js} +1 -1
  62. package/dist/assets/{diagram-OVF4WLC6-C5qBFdpm.js → diagram-OVF4WLC6-C3o3f1eL.js} +1 -1
  63. package/dist/assets/{diagram-PFPMY2P6-C1PW3U2P.js → diagram-PFPMY2P6-RRy9F9wH.js} +1 -1
  64. package/dist/assets/{diagram-UMYRVEAY-D2pa0Mzi.js → diagram-UMYRVEAY-CWfRKtkp.js} +1 -1
  65. package/dist/assets/{diagram-YEKJPTXX-BIzOA1li.js → diagram-YEKJPTXX-SlJKvYay.js} +1 -1
  66. package/dist/assets/{diagram-ZIFT7M5P-B1UL3O8m.js → diagram-ZIFT7M5P-DqzI8_eN.js} +1 -1
  67. package/dist/assets/{ebnfDiagram-VR2GEFS7-Cxu-kU8s.js → ebnfDiagram-VR2GEFS7-CDsrBNMu.js} +1 -1
  68. package/dist/assets/{elk-IJKZMXRS-DJS39Bm9.js → elk-IJKZMXRS-iikFqO-X.js} +1 -1
  69. package/dist/assets/{erDiagram-O2IAPWRE-Cb31BS5L.js → erDiagram-O2IAPWRE-DzFCc0Gp.js} +1 -1
  70. package/dist/assets/{file-up-DWGlVRGP.js → file-up-BzW-KJx-.js} +1 -1
  71. package/dist/assets/{flowDiagram-OXPTDLAJ-BaPTzS6m.js → flowDiagram-OXPTDLAJ-U5nLRus2.js} +1 -1
  72. package/dist/assets/{ganttDiagram-R7TSEDQI-B_Lr-26i.js → ganttDiagram-R7TSEDQI-h7xqrZ23.js} +1 -1
  73. package/dist/assets/{gitGraphDiagram-XJZIOB7I-UHyDFX6a.js → gitGraphDiagram-XJZIOB7I-Bo9ZX2g5.js} +1 -1
  74. package/dist/assets/{import-name-Cxwtm5XO.js → import-name-BN_-MYip.js} +1 -1
  75. package/dist/assets/{index-BSrb6nsc.js → index-BEjJ5c6H.js} +2 -2
  76. package/dist/assets/{index-BME9PdWM.js → index-DkDFNBNq.js} +1 -1
  77. package/dist/assets/{infoDiagram-5W2HQ5XZ-B7ovBJYA.js → infoDiagram-5W2HQ5XZ-CAZmFoiz.js} +1 -1
  78. package/dist/assets/{ishikawaDiagram-K3B6WC7H-BYS-TlrF.js → ishikawaDiagram-K3B6WC7H-T1M7DtvG.js} +1 -1
  79. package/dist/assets/{journeyDiagram-COXZFDF6-jJxKYEtT.js → journeyDiagram-COXZFDF6-DzO0Gzq9.js} +1 -1
  80. package/dist/assets/{kanban-definition-P3RFRI5V-DyiFYaIK.js → kanban-definition-P3RFRI5V-C0fM1UHu.js} +1 -1
  81. package/dist/assets/{mcp-servers-DXFuhTlS.js → mcp-servers-C7oRQpN5.js} +1 -1
  82. package/dist/assets/{mermaid.core-D3YJgsiB.js → mermaid.core-mKZTdBWz.js} +4 -4
  83. package/dist/assets/{mindmap-definition-LZFPQGKD-DnbWodUY.js → mindmap-definition-LZFPQGKD-DEgoC28p.js} +1 -1
  84. package/dist/assets/{minimize-Af1aU3Tz.js → minimize-D-CvaOV-.js} +1 -1
  85. package/dist/assets/{monitor-DuYHWIyw.js → monitor-DRcE92Rj.js} +1 -1
  86. package/dist/assets/{new-bot-TwOnvVO1.js → new-bot-COj1uEQa.js} +1 -1
  87. package/dist/assets/{org-library-BLD6YHcM.js → org-library-BIb28h24.js} +1 -1
  88. package/dist/assets/{package-format-72bbOy2w.js → package-format-hON5OXLZ.js} +1 -1
  89. package/dist/assets/{pegDiagram-BGZESJAR-CcOuP6zx.js → pegDiagram-BGZESJAR-Vatn56u_.js} +1 -1
  90. package/dist/assets/{pieDiagram-CAPJLFHJ-DhCU7Sfy.js → pieDiagram-CAPJLFHJ-DniOgFo3.js} +1 -1
  91. package/dist/assets/{quadrantDiagram-GDMTTRAM-7OziX26h.js → quadrantDiagram-GDMTTRAM-BqhAQt5w.js} +1 -1
  92. package/dist/assets/{rail-DI2iZJYt.js → rail-BWcrFQ2_.js} +1 -1
  93. package/dist/assets/{railroadDiagram-SM67HX2B-rg44R4U2.js → railroadDiagram-SM67HX2B-CxHs3JOs.js} +1 -1
  94. package/dist/assets/{remote-desktop-panel-BPKKvcHT.js → remote-desktop-panel-9XdtvJrd.js} +1 -1
  95. package/dist/assets/{requirementDiagram-X7JNWC4A-DFNvg72O.js → requirementDiagram-X7JNWC4A-ChjhMFon.js} +1 -1
  96. package/dist/assets/{sankeyDiagram-UM26HJRW-CUOrQF2i.js → sankeyDiagram-UM26HJRW-B67bYF8E.js} +1 -1
  97. package/dist/assets/{sequenceDiagram-ZO4K6R2Y-4HKsH1wt.js → sequenceDiagram-ZO4K6R2Y-CTy7B-eu.js} +1 -1
  98. package/dist/assets/{sizeCapture-INFHLROL-BEXQDTx-.js → sizeCapture-INFHLROL-uy6qiY-2.js} +1 -1
  99. package/dist/assets/{stateDiagram-v2-TBUQTH76-BYBLUqiy.js → stateDiagram-v2-TBUQTH76-BvYYPNqL.js} +1 -1
  100. package/dist/assets/{styles-C2_XZv1s.js → styles-DlOxUga6.js} +1 -1
  101. package/dist/assets/{swimlanes-N4OXWK64-D-bp8wWN.js → swimlanes-N4OXWK64-CMSAKu-2.js} +1 -1
  102. package/dist/assets/swimlanesDiagram-K3J5GTZL-BaaZdhur.js +8 -0
  103. package/dist/assets/{team-share-Dpu1nJNz.js → team-share-BbQqJ5O6.js} +1 -1
  104. package/dist/assets/{timeline-definition-YOQAKGHF-CtLFkcud.js → timeline-definition-YOQAKGHF-BOK1V5EC.js} +1 -1
  105. package/dist/assets/{usecaseDiagram-VIAY4XPW-AWOhUKvG.js → usecaseDiagram-VIAY4XPW-DEywfSfC.js} +1 -1
  106. package/dist/assets/{vennDiagram-BLWOH2XV-C_OYDBvp.js → vennDiagram-BLWOH2XV-DN1iWw97.js} +1 -1
  107. package/dist/assets/{vi-P4zxF4j0.js → vi-C-E3a4Tv.js} +2 -2
  108. package/dist/assets/{wardleyDiagram-YMQ3BMBF-CdxMh8tE.js → wardleyDiagram-YMQ3BMBF-BAIfK1We.js} +1 -1
  109. package/dist/assets/{xychartDiagram-TAQBALBS-BL2spC5z.js → xychartDiagram-TAQBALBS-BLBD8FaQ.js} +1 -1
  110. package/dist/index.html +2 -2
  111. package/dist/rail.html +4 -4
  112. package/dist-server/index.js +244 -229
  113. package/dist-server/openmausbot.js +1 -1
  114. package/dist-server/package.json +1 -1
  115. package/dist-server/pair-cli.js +1 -1
  116. package/dist-server/server/imported-skills.js +7 -2
  117. package/dist-server/server/index.js +7 -5
  118. package/dist-server/server/limix-bot-library.js +6 -0
  119. package/dist-server/server/limix-skill-hub.js +2 -2
  120. package/dist-server/server/limix-skill-store.js +179 -0
  121. package/dist-server/server/routes/bot-skills.js +15 -4
  122. package/package.json +1 -1
  123. package/skills/windows-computer-automation/SKILL.md +53 -0
  124. package/skills/windows-computer-automation/assets/HERIONIX-LEADER-PROMPT.md +25 -0
  125. package/skills/windows-computer-automation/assets/control-policy.example.json +25 -0
  126. package/skills/windows-computer-automation/assets/mcp-servers.example.json +19 -0
  127. package/skills/windows-computer-automation/manifest.json +27 -0
  128. package/skills/windows-computer-automation/references/herionix-integration.md +72 -0
  129. package/skills/windows-computer-automation/references/routing.md +26 -0
  130. package/skills/windows-computer-automation/references/scenarios.md +44 -0
  131. package/skills/windows-computer-automation/references/security.md +24 -0
  132. package/skills/windows-computer-automation/scripts/policy_engine.py +235 -0
  133. package/skills/windows-computer-automation/scripts/test_policy_engine.py +107 -0
  134. package/dist/assets/BotSettingsDialog-CVlvG4Rb.js +0 -16
  135. package/dist/assets/TeamLibraryPanel-DMhdlaSH.js +0 -1
  136. package/dist/assets/channel-k2J2GMwv.js +0 -1
  137. package/dist/assets/swimlanesDiagram-K3J5GTZL-DTQBdgKt.js +0 -8
@@ -4193,7 +4193,7 @@ string(),skillManifestEntrySchema),stagedSkillWriteSchema=external_exports.objec
4193
4193
  /^[a-f0-9]{64}$/).optional(),warnings:external_exports.array(external_exports.string()),skippedFiles:external_exports.array(external_exports.string()),createdAt:external_exports.string(),baseSha256:external_exports.string().regex(/^[a-f0-9]{64}$/).optional(),baseAppliedStageId:external_exports.string().optional()}).superRefine((entry,ctx)=>{entry.action==="update"&&(!entry.baseSha256||!entry.baseAppliedStageId)&&ctx.addIssue({code:"custom",message:"updated skills require their reviewed base revi\
4194
4194
  sion"})}),stagedStoreSchema=external_exports.object({writes:external_exports.record(external_exports.string(),stagedSkillWriteSchema)})}});import{dirname as dirname29,isAbsolute as isAbsolute14,join as join64,resolve as resolve22}from"node:path";var IMPORTED_SKILLS_DIR,INDEX_PATH,sha256Hex,entrySchema,indexSchema,choicesSchema2,init_imported_skills=__esm({"server/imported-skills.ts"(){"use strict";init_zod();init_skill_fit();init_skill_md();init_atomic();init_config3();init_skill_storage();init_skills();IMPORTED_SKILLS_DIR=join64(DATA_DIR,"imported-skills"),INDEX_PATH=join64(DATA_DIR,"skill-state","imported-skills.json"),sha256Hex=
4195
4195
  external_exports.string().regex(/^[a-f0-9]{64}$/),entrySchema=external_exports.object({description:external_exports.string(),source:external_exports.enum(["claudeCode","codex"]),root:external_exports.string(),origin:external_exports.string(),label:external_exports.string(),sha256:sha256Hex,filesSha256:sha256Hex,importedAt:external_exports.string(),warnings:external_exports.array(external_exports.string()),license:external_exports.string().optional(),compatibility:external_exports.string().optional()}),
4196
- indexSchema=external_exports.record(external_exports.string(),entrySchema),choicesSchema2=external_exports.object({off:external_exports.array(external_exports.string()),on:external_exports.record(external_exports.string(),sha256Hex).default({}),listed:external_exports.array(external_exports.string()).optional()})}});function parseToml(source){let text13=source.charCodeAt(0)===65279?source.slice(1):source,i2=0,root=table(),current=root,fail3=message5=>{throw new TomlError(`line ${text13.slice(0,i2).split(`
4196
+ indexSchema=external_exports.record(external_exports.string(),entrySchema),choicesSchema2=external_exports.object({off:external_exports.array(external_exports.string()),on:external_exports.record(external_exports.string(),sha256Hex).default({}),listed:external_exports.array(external_exports.string()).optional(),picked:external_exports.array(external_exports.string()).optional()})}});function parseToml(source){let text13=source.charCodeAt(0)===65279?source.slice(1):source,i2=0,root=table(),current=root,fail3=message5=>{throw new TomlError(`line ${text13.slice(0,i2).split(`
4197
4197
  `).length}: ${message5}`)},spaces=()=>{for(;text13[i2]===" "||text13[i2]===" ";)i2++},comment=()=>{if(text13[i2]==="#")for(;i2<text13.length&&text13[i2]!==`
4198
4198
  `;)i2++},newline=()=>{if(text13[i2]===`
4199
4199
  `)i2+=1;else if(text13[i2]==="\r"&&text13[i2+1]===`
@@ -1 +1 @@
1
- {"type":"module","version":"0.3.24"}
1
+ {"type":"module","version":"0.3.25"}
@@ -4192,7 +4192,7 @@ string(),skillManifestEntrySchema),stagedSkillWriteSchema=external_exports.objec
4192
4192
  /^[a-f0-9]{64}$/).optional(),warnings:external_exports.array(external_exports.string()),skippedFiles:external_exports.array(external_exports.string()),createdAt:external_exports.string(),baseSha256:external_exports.string().regex(/^[a-f0-9]{64}$/).optional(),baseAppliedStageId:external_exports.string().optional()}).superRefine((entry,ctx)=>{entry.action==="update"&&(!entry.baseSha256||!entry.baseAppliedStageId)&&ctx.addIssue({code:"custom",message:"updated skills require their reviewed base revi\
4193
4193
  sion"})}),stagedStoreSchema=external_exports.object({writes:external_exports.record(external_exports.string(),stagedSkillWriteSchema)})}});import{dirname as dirname29,isAbsolute as isAbsolute14,join as join64,resolve as resolve22}from"node:path";var IMPORTED_SKILLS_DIR,INDEX_PATH,sha256Hex,entrySchema,indexSchema,choicesSchema2,init_imported_skills=__esm({"server/imported-skills.ts"(){"use strict";init_zod();init_skill_fit();init_skill_md();init_atomic();init_config3();init_skill_storage();init_skills();IMPORTED_SKILLS_DIR=join64(DATA_DIR,"imported-skills"),INDEX_PATH=join64(DATA_DIR,"skill-state","imported-skills.json"),sha256Hex=
4194
4194
  external_exports.string().regex(/^[a-f0-9]{64}$/),entrySchema=external_exports.object({description:external_exports.string(),source:external_exports.enum(["claudeCode","codex"]),root:external_exports.string(),origin:external_exports.string(),label:external_exports.string(),sha256:sha256Hex,filesSha256:sha256Hex,importedAt:external_exports.string(),warnings:external_exports.array(external_exports.string()),license:external_exports.string().optional(),compatibility:external_exports.string().optional()}),
4195
- indexSchema=external_exports.record(external_exports.string(),entrySchema),choicesSchema2=external_exports.object({off:external_exports.array(external_exports.string()),on:external_exports.record(external_exports.string(),sha256Hex).default({}),listed:external_exports.array(external_exports.string()).optional()})}});function parseToml(source){let text13=source.charCodeAt(0)===65279?source.slice(1):source,i2=0,root=table(),current=root,fail3=message5=>{throw new TomlError(`line ${text13.slice(0,i2).split(`
4195
+ indexSchema=external_exports.record(external_exports.string(),entrySchema),choicesSchema2=external_exports.object({off:external_exports.array(external_exports.string()),on:external_exports.record(external_exports.string(),sha256Hex).default({}),listed:external_exports.array(external_exports.string()).optional(),picked:external_exports.array(external_exports.string()).optional()})}});function parseToml(source){let text13=source.charCodeAt(0)===65279?source.slice(1):source,i2=0,root=table(),current=root,fail3=message5=>{throw new TomlError(`line ${text13.slice(0,i2).split(`
4196
4196
  `).length}: ${message5}`)},spaces=()=>{for(;text13[i2]===" "||text13[i2]===" ";)i2++},comment=()=>{if(text13[i2]==="#")for(;i2<text13.length&&text13[i2]!==`
4197
4197
  `;)i2++},newline=()=>{if(text13[i2]===`
4198
4198
  `)i2+=1;else if(text13[i2]==="\r"&&text13[i2+1]===`
@@ -47,6 +47,8 @@ const choicesSchema = z.object({
47
47
  off: z.array(z.string()),
48
48
  on: z.record(z.string(), sha256Hex).default({}),
49
49
  listed: z.array(z.string()).optional(),
50
+ /** Limix Bot: skills from this computer are off for a bot until a person adds one from its store. */
51
+ picked: z.array(z.string()).optional(),
50
52
  });
51
53
  /** Every imported skill by name; empty when there are none or the index cannot be trusted. */
52
54
  export function readImportedSkills() {
@@ -279,6 +281,8 @@ export function storeImportedSkills(inputs) {
279
281
  function enabledFor(name, entry, choices) {
280
282
  if (choices.off.includes(name))
281
283
  return false;
284
+ if (!(choices.picked ?? []).includes(name))
285
+ return false;
282
286
  return entry.warnings.length === 0 || choices.on[name] === entry.filesSha256;
283
287
  }
284
288
  const comparable = (path) => {
@@ -361,9 +365,10 @@ export function setImportedSkillEnabled(botId, name, enabled) {
361
365
  const off = choices.off.filter((other) => other !== name);
362
366
  const { [name]: _read, ...on } = choices.on;
363
367
  const listed = choices.listed?.filter((other) => other !== name);
368
+ const picked = (choices.picked ?? []).filter((other) => other !== name);
364
369
  const next = enabled
365
- ? { off, on: entry.warnings.length ? { ...on, [name]: entry.filesSha256 } : on, ...(listed ? { listed: [...listed, name].sort() } : {}) }
366
- : { off: [...off, name].sort(), on, ...(listed ? { listed } : {}) };
370
+ ? { off, on: entry.warnings.length ? { ...on, [name]: entry.filesSha256 } : on, ...(listed ? { listed: [...listed, name].sort() } : {}), picked: [...picked, name].sort() }
371
+ : { off: [...off, name].sort(), on, ...(listed ? { listed } : {}), picked };
367
372
  mkdirSync(dirname(choicesPath(botId)), { recursive: true, mode: 0o700 });
368
373
  writeFileAtomic(choicesPath(botId), `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
369
374
  return importedSkillListings(botId, new Set()).find((skill) => skill.name === name);
@@ -442,7 +442,7 @@ import { AGENTS_NODE_FLAG, SPAWNED_PROXIES } from "./proxy-paths.js";
442
442
  import { agentsProxyLaunch, agentsToolBridgeEnv, applyAgentsBridge, bridgedToolTitle, turnEngine } from "./agents-bridge-switch.js";
443
443
  import { agentsServerEnv, botDrivenConversation, COMPUTER_OFF_REFUSAL, computerPickRefusal, directTurnKind, roomSkillsRefusal } from "./agents-turn.js";
444
444
  import { harnessMcpTokenEnv } from "./harness-mcp-token.js";
445
- import { loadBundledSkills, loadUserSkills, mergeSkills, renderSkillInstructions, selectBundledSkills, skillsLibraryEnabled, readSkillLibraryIndex, } from "./skill-library.js";
445
+ import { loadBundledSkills, loadUserSkills, mergeSkills, renderSkillInstructions, skillsLibraryEnabled, readSkillLibraryIndex, } from "./skill-library.js";
446
446
  import { runSkillsLibraryBootSweep } from "./skills-library-migration.js";
447
447
  import { installedPlaybookInstructions } from "./installed-playbooks.js";
448
448
  import { picture as sharedPicture } from "./package-export.js";
@@ -512,6 +512,7 @@ import { createBotOrderRoutes } from "./routes/bot-order.js";
512
512
  import { createBotLifecycleRoutes } from "./routes/bot-lifecycle.js";
513
513
  import { createBotSkillsRoutes } from "./routes/bot-skills.js";
514
514
  import { startHubAutoSync } from "./limix-skill-hub.js";
515
+ import { limixBundledFor, limixSelectBundled, limixSkillDefaultsMigration } from "./limix-skill-store.js";
515
516
  import { createSkillShareRoutes } from "./routes/skill-share.js";
516
517
  import { createTeamContextRoutes } from "./routes/team-context.js";
517
518
  import { createAppSuggestions } from "./host/app-suggestions.js";
@@ -1436,6 +1437,7 @@ function resyncOpenCodeProviderKeys() {
1436
1437
  const firstEngineRead = holdUntilFirstEngineRead();
1437
1438
  await registry.load(providerConfigs(), decorateHostedProvider);
1438
1439
  const bundledSkills = loadBundledSkills();
1440
+ limixSkillDefaultsMigration();
1439
1441
  const availableSkills = () => mergeSkills(bundledSkills, loadUserSkills(join(DATA_DIR, "skills")));
1440
1442
  /** Every turn the engine can mount it, whatever its words: they pick only the skill's instructions. A Cloud home or a hosted workspace has no USB port. */
1441
1443
  const usbPhoneOffered = (instance, skills) => instance.adapter.capabilities.phoneMcp === true &&
@@ -8084,8 +8086,8 @@ async function beginTurn(botId, text, opts) {
8084
8086
  handoffs.begin(threadId, dispatchClaimId, dispatchContext.handoff);
8085
8087
  const integrations = {};
8086
8088
  let browser = null;
8087
- const skills = availableSkills(), phoneOffered = usbPhoneOffered(instance, skills) && !proactive.fence.confines(threadId) && !botApiThread(threadId);
8088
- const selectedSkills = selectBundledSkills(providerText, [
8089
+ const skills = limixBundledFor(bot.id, availableSkills()), phoneOffered = usbPhoneOffered(instance, skills) && !proactive.fence.confines(threadId) && !botApiThread(threadId);
8090
+ const selectedSkills = limixSelectBundled(providerText, [
8089
8091
  ...(phoneOffered ? ["phoneMcp"] : []),
8090
8092
  ...(skillAuthoring ? ["skillAuthoring"] : []),
8091
8093
  ], skills);
@@ -10048,8 +10050,8 @@ setupRetry = 0) {
10048
10050
  : undefined, bot.id);
10049
10051
  const roomContext = roomWindow.text;
10050
10052
  const turnImages = usesNativeImageInput ? [...burstImages, ...resolvedLatestImages.images, ...await videoFramesWithin(burstImages.length + resolvedLatestImages.images.length, resolvedLatestImages.videos)] : [];
10051
- const skills = availableSkills(), phoneOffered = usbPhoneOffered(instance, skills);
10052
- const selectedSkills = mergeSkills(selectBundledSkills(roomContext, phoneOffered ? ["phoneMcp"] : [], skills), selectBundledSkills(latestUser?.text ?? "", skillAuthoring ? ["skillAuthoring"] : [], skills));
10053
+ const skills = limixBundledFor(bot.id, availableSkills()), phoneOffered = usbPhoneOffered(instance, skills);
10054
+ const selectedSkills = mergeSkills(limixSelectBundled(roomContext, phoneOffered ? ["phoneMcp"] : [], skills), limixSelectBundled(latestUser?.text ?? "", skillAuthoring ? ["skillAuthoring"] : [], skills));
10053
10055
  if (phoneOffered)
10054
10056
  integrations.phone = phoneIntegration(bot.id, threadId, internalGeneration);
10055
10057
  try {
@@ -123,6 +123,12 @@ export function parseLibraryBot(value, expectedId) {
123
123
  schedule: { type: "daily", time, weekdays: [...new Set(weekdays)].sort() }, enabled: false, overlap: "skip" };
124
124
  });
125
125
  }
126
+ if (value.skills !== undefined) {
127
+ if (!Array.isArray(value.skills) || value.skills.length > 20 || value.skills.some((name) => typeof name !== "string" || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name) || name.length > 64)) {
128
+ throw new Error("skills is invalid");
129
+ }
130
+ bot.skills = [...new Set(value.skills)];
131
+ }
126
132
  return bot;
127
133
  }
128
134
  async function fetchJson(url, maxBytes) {
@@ -1,6 +1,6 @@
1
1
  // Limix Skill Hub client: keeps a cached copy of the hub catalog, installs a skill only after every file matches the
2
2
  // SHA-256 the catalog pins, and never installs scripts, binaries or MCP files unless the owner allows tools for that
3
- // one skill. Installed skills land DISABLED; the owner turns them on. Auto Sync (catalog refresh) is on by default,
3
+ // one skill. Installed skills wait in the store ("Kho Kĩ Năng (Skills)"), switched on but on no bot until one is added to it. Auto Sync (catalog refresh) is on by default,
4
4
  // Auto Install is off by default.
5
5
  import { createHash } from "node:crypto";
6
6
  import { existsSync, mkdirSync, readFileSync } from "node:fs";
@@ -171,7 +171,7 @@ export async function installHubSkill(id, options = {}) {
171
171
  ...(entry.tags.length ? { tags: entry.tags } : {}),
172
172
  warnings: entry.notes,
173
173
  skippedFiles: skipped,
174
- reviewState: "disabled",
174
+ reviewState: "approved",
175
175
  ...(files.length ? { files } : {}),
176
176
  });
177
177
  if ("error" in result)
@@ -0,0 +1,179 @@
1
+ // Limix Bot: skills are chosen per bot. A new bot starts with only Limix's core skills (learning new skills, reading
2
+ // links with Agent Reach, operating Windows, the phone helpers) plus the skills it learns itself; everything else waits
3
+ // in the store ("Kho Kĩ Năng (Skills)") until a person adds it to one bot. Settings → Kĩ Năng (Skills) only downloads and
4
+ // installs skills into the store; it never turns a skill on for every bot.
5
+ //
6
+ // Three kinds of skill can be added to a bot from the store:
7
+ // bundled — shipped in skills/<id> (ui-ux-pro-max, data-dashboards…): the bot's picks live in
8
+ // <data>/skill-state/<bot>/limix-bundled.json; core ones are always on and cannot be removed.
9
+ // library — installed from the Limix Skill Hub or pasted in Settings: the bot's assignedSkills.
10
+ // imported — brought in from this computer's Claude Code or Codex: the bot's own switch (opt-in in Limix).
11
+ // `/name` calls any skill the bot has, bundled ones included (limixSelectBundled).
12
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
13
+ import { join } from "node:path";
14
+ import { isSkillName } from "../shared/skill-md.js";
15
+ import { writeFileAtomic } from "./atomic.js";
16
+ import { DATA_DIR } from "./config.js";
17
+ import { importedSkillListings, setImportedSkillEnabled } from "./imported-skills.js";
18
+ import { listLibrarySkills, loadBundledSkills, mergeSkills, readSkillLibraryIndex, selectBundledSkills, setLibrarySkillEveryBot, setLibrarySkillOptOut, setLibrarySkillReviewState, unassignLibrarySkill, } from "./skill-library.js";
19
+ /** Always on for every bot. "Learning new skills" itself is the skill-authoring feature, on for every bot already. */
20
+ export const LIMIX_CORE_SKILLS = new Set([
21
+ "create-verification-skill", "agent-reach", "windows-computer-automation", "phone-connection", "phone-harness",
22
+ ]);
23
+ const BOT_ID = /^[\w-]+$/;
24
+ const picksPath = (botId) => join(DATA_DIR, "skill-state", botId, "limix-bundled.json");
25
+ export function bundledPicks(botId) {
26
+ if (!BOT_ID.test(botId))
27
+ return new Set();
28
+ try {
29
+ const parsed = JSON.parse(readFileSync(picksPath(botId), "utf8"));
30
+ return new Set(Array.isArray(parsed) ? parsed.filter((name) => typeof name === "string" && isSkillName(name)) : []);
31
+ }
32
+ catch {
33
+ return new Set();
34
+ }
35
+ }
36
+ function setBundledPick(botId, id, on) {
37
+ if (!BOT_ID.test(botId) || !isSkillName(id))
38
+ return;
39
+ const picks = bundledPicks(botId);
40
+ if (picks.has(id) === on)
41
+ return;
42
+ if (on)
43
+ picks.add(id);
44
+ else
45
+ picks.delete(id);
46
+ mkdirSync(join(DATA_DIR, "skill-state", botId), { recursive: true, mode: 0o700 });
47
+ writeFileAtomic(picksPath(botId), `${JSON.stringify([...picks].sort(), null, 2)}\n`, { mode: 0o600 });
48
+ }
49
+ /** The shipped skills one bot has: the core ones and the ones a person added to it. */
50
+ export function limixBundledFor(botId, skills) {
51
+ const picks = bundledPicks(botId);
52
+ return skills.filter(({ manifest }) => LIMIX_CORE_SKILLS.has(manifest.id) || picks.has(manifest.id));
53
+ }
54
+ const slashCalled = (text, id) => new RegExp(`(^|\\s)/${id.replace(/[-]/g, "\\-")}(?=$|[\\s.,:;!?])`, "m").test(text);
55
+ /** selectBundledSkills, plus every skill of `skills` the text calls by `/id`. */
56
+ export function limixSelectBundled(text, capabilities, skills) {
57
+ const available = new Set(capabilities);
58
+ const called = skills.filter(({ manifest }) => manifest.requiredCapabilities.every((capability) => available.has(capability)) && slashCalled(text, manifest.id));
59
+ return mergeSkills(selectBundledSkills(text, available, skills), called);
60
+ }
61
+ /** The bot's shipped skills as rows of its Skills page. */
62
+ export function limixBundledListings(botId) {
63
+ return limixBundledFor(botId, loadBundledSkills()).map(({ manifest }) => ({
64
+ name: manifest.id, description: manifest.description, enabled: true, editable: false,
65
+ source: LIMIX_CORE_SKILLS.has(manifest.id) ? "Kĩ năng gốc của Limix Bot" : "Kho Kĩ Năng (Skills) · có sẵn",
66
+ sha256: "", importedAt: "", warnings: [], skippedFiles: [], origin: "bundled", core: LIMIX_CORE_SKILLS.has(manifest.id),
67
+ }));
68
+ }
69
+ /** A bot's switch for one of its shipped skills: off takes an added one away; a core one stays. Null: not shipped. */
70
+ export function limixBundledSwitch(botId, name, enabled) {
71
+ if (!loadBundledSkills().some(({ manifest }) => manifest.id === name))
72
+ return null;
73
+ if (LIMIX_CORE_SKILLS.has(name)) {
74
+ return enabled ? { status: 200, body: { skill: limixBundledListings(botId).find((skill) => skill.name === name) } }
75
+ : { status: 409, body: { error: "Kĩ năng gốc của Limix Bot luôn bật cho mọi bot." } };
76
+ }
77
+ setBundledPick(botId, name, enabled);
78
+ return { status: 200, body: { skill: { ...limixBundledListings(botId).find((skill) => skill.name === name), name, enabled } } };
79
+ }
80
+ function storeItems(bots, botId) {
81
+ const bot = bots.bot(botId);
82
+ const assigned = new Set(bot.assignedSkills ?? []);
83
+ const picks = bundledPicks(botId);
84
+ const items = new Map();
85
+ for (const { manifest } of loadBundledSkills()) {
86
+ const core = LIMIX_CORE_SKILLS.has(manifest.id);
87
+ items.set(manifest.id, { name: manifest.id, description: manifest.description, kind: "bundled", added: core || picks.has(manifest.id), core, ready: true, warnings: 0, source: "Có sẵn trong Limix Bot" });
88
+ }
89
+ for (const skill of listLibrarySkills()) {
90
+ if (items.has(skill.name))
91
+ continue;
92
+ const intact = !skill.warnings.some((warning) => warning.includes("enablement is blocked"));
93
+ items.set(skill.name, {
94
+ name: skill.name, description: skill.description, kind: "library", added: assigned.has(skill.name) && skill.enabled, core: false,
95
+ ready: intact, warnings: skill.warnings.length, source: skill.source.startsWith("limix-hub:") ? "Thư viện kĩ năng Limix" : "Đã cài trong Cài đặt",
96
+ });
97
+ }
98
+ for (const skill of importedSkillListings(botId, new Set())) {
99
+ if (items.has(skill.name))
100
+ continue;
101
+ items.set(skill.name, { name: skill.name, description: skill.description, kind: "imported", added: skill.enabled, core: false, ready: true, warnings: skill.warnings.length, source: skill.source || "Từ máy tính này" });
102
+ }
103
+ return [...items.values()].sort((a, b) => Number(b.core) - Number(a.core) || a.name.localeCompare(b.name));
104
+ }
105
+ /** Add one skill to one bot, or take it away. `kind` may be left out: the store finds the name. */
106
+ export function limixStoreSet(bots, botId, name, add, kind) {
107
+ const bot = bots.bot(botId);
108
+ if (!bot)
109
+ return { error: "Không tìm thấy bot." };
110
+ if (!isSkillName(name))
111
+ return { error: "Tên kĩ năng không hợp lệ." };
112
+ const item = storeItems(bots, botId).find((entry) => entry.name === name && (!kind || entry.kind === kind));
113
+ if (!item)
114
+ return { error: `Kho chưa có kĩ năng "${name}".` };
115
+ if (item.kind === "bundled") {
116
+ if (item.core)
117
+ return add ? { ok: true } : { error: "Kĩ năng gốc của Limix Bot luôn bật cho mọi bot." };
118
+ setBundledPick(botId, name, add);
119
+ return { ok: true };
120
+ }
121
+ if (item.kind === "imported") {
122
+ const result = setImportedSkillEnabled(botId, name, add);
123
+ return "error" in result ? { error: result.error } : { ok: true };
124
+ }
125
+ const assigned = bot.assignedSkills ?? [];
126
+ if (add) {
127
+ // Adding from the store is the person's choice for this bot, so a skill installed but not yet switched on is switched on.
128
+ const listing = listLibrarySkills().find((skill) => skill.name === name);
129
+ if (listing && !listing.enabled) {
130
+ const approved = setLibrarySkillReviewState(name, "approved");
131
+ if ("error" in approved)
132
+ return { error: approved.error };
133
+ }
134
+ setLibrarySkillOptOut(botId, name, false);
135
+ if (!assigned.includes(name))
136
+ bots.patchBot(botId, { assignedSkills: [...assigned, name] });
137
+ return { ok: true };
138
+ }
139
+ const result = unassignLibrarySkill(botId, name, assigned);
140
+ if ("unassigned" in result && result.assignedSkills)
141
+ bots.patchBot(botId, { assignedSkills: result.assignedSkills });
142
+ return { ok: true };
143
+ }
144
+ /** GET /api/limix/skill-store/<bot> lists the store for that bot; POST {name, add, kind?} adds or takes away one skill. */
145
+ export function handleLimixSkillStore(bots, method, path, body) {
146
+ const m = path.match(/^\/api\/limix\/skill-store\/([\w-]+)$/);
147
+ if (!m)
148
+ return null;
149
+ const botId = m[1];
150
+ if (!bots.bot(botId))
151
+ return { status: 404, body: { error: "Không tìm thấy bot." } };
152
+ if (method === "GET")
153
+ return { status: 200, body: { skills: storeItems(bots, botId) } };
154
+ if (method === "POST") {
155
+ const raw = (body && typeof body === "object" ? body : {});
156
+ const kind = raw.kind === "bundled" || raw.kind === "library" || raw.kind === "imported" ? raw.kind : undefined;
157
+ if (typeof raw.name !== "string" || typeof raw.add !== "boolean")
158
+ return { status: 400, body: { error: "Cần tên kĩ năng và add." } };
159
+ const result = limixStoreSet(bots, botId, raw.name, raw.add, kind);
160
+ return "error" in result ? { status: 422, body: result } : { status: 200, body: { skills: storeItems(bots, botId) } };
161
+ }
162
+ return { status: 405, body: { error: "method not allowed" } };
163
+ }
164
+ /** Once: skills that earlier versions turned on for every bot go back to the store. */
165
+ export function limixSkillDefaultsMigration() {
166
+ const marker = join(DATA_DIR, "skill-state", "limix-skill-defaults-v1.json");
167
+ if (existsSync(marker))
168
+ return;
169
+ try {
170
+ for (const [name, entry] of Object.entries(readSkillLibraryIndex()))
171
+ if (entry.everyBot)
172
+ setLibrarySkillEveryBot(name, false);
173
+ mkdirSync(join(DATA_DIR, "skill-state"), { recursive: true, mode: 0o700 });
174
+ writeFileAtomic(marker, `${JSON.stringify({ at: new Date().toISOString() })}\n`, { mode: 0o600 });
175
+ }
176
+ catch {
177
+ // Retried at the next start.
178
+ }
179
+ }
@@ -7,6 +7,7 @@ import { fetchSkillFromSource } from "../skill-fetch.js";
7
7
  import { readLocalSkills } from "../limix-skill-folder.js";
8
8
  import { loadBundledSkills } from "../skill-library.js";
9
9
  import { handleLimixHub } from "../limix-skill-hub.js";
10
+ import { handleLimixSkillStore, LIMIX_CORE_SKILLS, limixBundledListings, limixBundledSwitch } from "../limix-skill-store.js";
10
11
  import { skillImportSummary } from "../skill-folder.js";
11
12
  import { installLibrarySkill, librarySkillOptOuts, listLibrarySkills, readLibrarySkillFile, readSkillLibraryIndex, setLibrarySkillEveryBot, setLibrarySkillOptOut, setLibrarySkillReviewState, skillsLibraryEnabled, unassignLibrarySkill } from "../skill-library.js";
12
13
  import { installSkill, listBotSkillsWithOrigin, listStagedSkillWrites, parseSkillMd, removeSkill, scanSkillText, setSkillEnabled, } from "../skills.js";
@@ -25,8 +26,13 @@ export function createBotSkillsRoutes(deps) {
25
26
  if (hub)
26
27
  return json(res, hub.status, hub.body);
27
28
  }
29
+ if (path.startsWith("/api/limix/skill-store/")) {
30
+ const skillStore = handleLimixSkillStore(store, method, path, method === "POST" ? await readBody(req) : undefined);
31
+ if (skillStore)
32
+ return json(res, skillStore.status, skillStore.body);
33
+ }
28
34
  if (method === "GET" && path === "/api/limix/bundled-skills") {
29
- const skills = loadBundledSkills().map(({ manifest }) => ({ id: manifest.id, name: manifest.name, description: manifest.description, defaultEnabled: manifest.defaultEnabled, triggerTerms: manifest.triggerTerms }));
35
+ const skills = loadBundledSkills().map(({ manifest }) => ({ id: manifest.id, name: manifest.name, description: manifest.description, defaultEnabled: manifest.defaultEnabled, triggerTerms: manifest.triggerTerms, core: LIMIX_CORE_SKILLS.has(manifest.id) }));
30
36
  return json(res, 200, { skills });
31
37
  }
32
38
  // Imports land DISABLED: a person enables one after reading SKILL.md and its scan warnings.
@@ -63,14 +69,15 @@ export function createBotSkillsRoutes(deps) {
63
69
  const { skills, library } = listBotSkillsWithOrigin(m[1], store.bot(m[1]).assignedSkills);
64
70
  const names = new Set(skills.map((skill) => skill.name));
65
71
  return json(res, 200, {
66
- skills: [...skills, ...listBotSkills(bot, threadId).filter((skill) => !names.has(skill.name))],
72
+ skills: [...skills, ...listBotSkills(bot, threadId).filter((skill) => !names.has(skill.name)),
73
+ ...limixBundledListings(bot.id).filter((skill) => !names.has(skill.name) && !listBotSkills(bot, threadId).some((other) => other.name === skill.name))],
67
74
  library,
68
75
  assignedSkills: store.bot(m[1]).assignedSkills,
69
76
  staged: listStagedSkillWrites(m[1]).map(stagedSkillListing),
70
77
  });
71
78
  }
72
79
  return json(res, 200, {
73
- skills: listBotSkills(bot, threadId),
80
+ skills: [...listBotSkills(bot, threadId), ...limixBundledListings(bot.id)],
74
81
  staged: listStagedSkillWrites(m[1]).map(stagedSkillListing),
75
82
  });
76
83
  }
@@ -121,8 +128,12 @@ export function createBotSkillsRoutes(deps) {
121
128
  return json(res, 200, { skill });
122
129
  }
123
130
  const result = setSkillEnabled(m[1], m[2], parsed.data.enabled);
124
- if ("error" in result)
131
+ if ("error" in result) {
132
+ const shipped = limixBundledSwitch(m[1], m[2], parsed.data.enabled);
133
+ if (shipped)
134
+ return json(res, shipped.status, shipped.body);
125
135
  return json(res, 404, { error: result.error });
136
+ }
126
137
  return json(res, 200, { skill: result });
127
138
  }
128
139
  if (m && method === "DELETE") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "limixbot",
3
- "version": "0.3.24",
3
+ "version": "0.3.25",
4
4
  "description": "Run the Limix Bot server anywhere and pair your devices to it (Leox ৹ Limix AI)",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: windows-computer-automation
3
+ description: Điều phối AI Agent hoặc Bot điều khiển Windows trong Herionix qua Agent Desktop và Windows-MCP. Dùng khi cần đọc màn hình, thao tác cửa sổ, nhập liệu, trình duyệt, quản lý ứng dụng, file hoặc tiến trình; lựa chọn tool phù hợp, quản lý quyền, tránh bot giành chuột, xác minh kết quả và chống lặp thao tác. Hỗ trợ thiết kế hoặc khắc phục tích hợp hai MCP Server vào ứng dụng AI nhiều agent.
4
+ ---
5
+
6
+ # Windows Computer Automation — Herionix
7
+
8
+ ## Mục tiêu
9
+
10
+ Dùng **Agent Desktop** cho phần tử giao diện Windows dựa trên accessibility tree; dùng **Windows-MCP** cho công cụ hệ thống, filesystem, app, process và dự phòng UI khi thích hợp. Đây là Skill hướng dẫn điều phối: **không tự cài MCP Server hoặc cấp quyền cho máy**. Chỉ thực thi khi Herionix đã kết nối thực tế với các MCP Server và tool được cấp phép.
11
+
12
+ ## Quy trình bắt buộc
13
+
14
+ 1. **Xác định nhiệm vụ và giới hạn:** Xác nhận ứng dụng/cửa sổ, đầu vào/đầu ra, đường dẫn file, hành động cuối cùng có được phép thực hiện không. Phân biệt chỉ đọc với thay đổi hệ thống.
15
+ 2. **Khám phá năng lực thật:** Lấy `list_tools` của hai MCP Server; không giả định tool có mặt chỉ vì tài liệu có tên. Kiểm tra trạng thái desktop, hệ điều hành, phiên đăng nhập và quyền. Nếu thiếu connector, dừng và thông báo cụ thể; không giả vờ đã thao tác.
16
+ 3. **Kiểm tra rủi ro:** Đọc [references/security.md](references/security.md). Những tác vụ xóa/ghi đè, chạy lệnh, sửa Registry, gửi nội dung ra ngoài, thanh toán, đổi quyền hoặc tắt tiến trình cần quyết định theo policy của host. Model **không tự phê duyệt**. Host phải chặn hành động trước khi gọi tool.
17
+ 4. **Điều phối một desktop:** Một `desktop_id` chỉ được có **một agent giữ quyền ghi/nhập liệu** tại một thời điểm; khóa ở backend Herionix, không chỉ trong prompt. Nhiều agent có thể lập kế hoạch/đọc nếu đọc không cản trở; việc ghi phải đi qua hàng đợi.
18
+ 5. **Quan sát trước khi thao tác:** Ưu tiên accessibility tree, ref và window ID; tránh tọa độ nếu còn lựa chọn định danh ổn định. Chọn provider theo bảng dưới đây và [references/routing.md](references/routing.md).
19
+ 6. **Thực thi tối thiểu:** Mỗi lần chỉ thực thi bước cần thiết. Không gọi song song hai tool nhập liệu. Sử dụng key chống trùng `task_id + step_id + target + desired_effect` do backend cấp.
20
+ 7. **Xác minh sau thao tác:** Đọc lại phần tử/trạng thái/file hoặc kết quả tương ứng, xác nhận hậu điều kiện. `tool returned success` không đồng nghĩa công việc đã hoàn thành.
21
+ 8. **Phục hồi có giới hạn:** Với `STALE_REF`/`AMBIGUOUS_TARGET`, làm mới snapshot và định vị lại. Chỉ retry khi upstream công bố an toàn hoặc hậu điều kiện chứng minh chưa xảy ra tác dụng phụ. Dừng sau tối đa **1 retry kỹ thuật** cho một bước, hoặc **2 chu kỳ không tiến triển**; không gọi vòng lặp vô hạn, không tự `microcompact` để che lỗi.
22
+ 9. **Báo cáo:** Trả lời ngắn bằng tiếng Việt: việc đã làm/đã xác minh, file được tạo (nếu có), bước chưa hoàn tất, các phê duyệt cần thiết, lỗi và bằng chứng không nhạy cảm. Không ghi log ảnh/chụp clipboard chứa bí mật.
23
+
24
+ ## Chọn công cụ
25
+
26
+ | Tình huống | Ưu tiên | Dự phòng / điều kiện |
27
+ |---|---|---|
28
+ | Đọc/nhấn/nhập trên UI có ref định danh | Agent Desktop: `desktop_snapshot`, `desktop_click`, `desktop_type`... | Windows-MCP `Snapshot` + `Click`/`Type` sau khi định vị mới |
29
+ | Giao diện không xuất accessibility hoặc điều khiển tọa độ | Windows-MCP `Snapshot` / `Screenshot`, `Click`, `Type` | Agent Desktop `--headed` chỉ khi cần và được phép |
30
+ | Khởi động, chuyển cửa sổ ứng dụng | Windows-MCP `App` | Agent Desktop `desktop_launch` / window tools nếu có |
31
+ | Xem, ghi file trong thư mục được cho phép | Windows-MCP `FileSystem` | Không thay bằng UI nếu có API/file-tool tin cậy |
32
+ | Kiểm tra tiến trình | Windows-MCP `Process` (chỉ list) | Terminate yêu cầu xác nhận từ host |
33
+ | PowerShell / Registry | **Mặc định không mở quyền** | Chỉ bật có chủ đích trong môi trường kiểm soát, qua host approval |
34
+ | Trang web cần DOM | Windows-MCP `Snapshot` (`use_dom` nếu phiên bản hỗ trợ) | Với luồng web phức tạp, cân nhắc browser automation chuyên biệt |
35
+
36
+ **Không chuyển provider để retry mù:** Sau lỗi của một thao tác có tác dụng phụ, phải quan sát và xác minh trạng thái trước khi quyết định bước tiếp theo.
37
+
38
+ ## Luồng chống tốn token
39
+
40
+ - Lưu `window_id`, `snapshot_id`, mục tiêu và tóm tắt trạng thái ngắn; **vô hiệu** ref cũ sau mỗi thay đổi UI.
41
+ - Không gửi ảnh toàn màn hình ở mọi bước; dùng tree compact, vùng chụp nhỏ hoặc snapshot theo app.
42
+ - Chỉ tiếp tục khi có `progress marker` mới (trạng thái thay đổi, file xuất hiện, nút đổi trạng thái, trang chuyển); nếu hai lần không có tiến triển, dừng và báo lỗi.
43
+ - Nếu hai agent cùng xin điều khiển, chỉ một agent thực thi; agent còn lại chờ hoặc làm việc không tác động desktop.
44
+
45
+ ## Kết nối và kiểm thử
46
+
47
+ - Tham khảo [references/herionix-integration.md](references/herionix-integration.md) để cài và kết nối hai MCP Server qua stdio.
48
+ - Dùng [assets/mcp-servers.example.json](assets/mcp-servers.example.json) làm cấu hình minh họa, **không coi là file cấu hình Herionix chính thức**.
49
+ - Có thể dùng `scripts/policy_engine.py` làm ví dụ bộ định tuyến/rào chắn **không có quyền tự thực thi OS**; backend phải thực hiện kiểm tra policy, khóa và approval thật. Ví dụ và kiểm thử: [references/scenarios.md](references/scenarios.md).
50
+
51
+ ## Cách ứng xử khi không có quyền điều khiển máy
52
+
53
+ Trình bày kế hoạch, lệnh cấu hình hoặc code phù hợp; nói rõ chưa thi hành và chưa xác minh trên Windows thật. Không tuyên bố đã tích hợp vào máy hay Herionix nếu chưa có truy cập đến mã nguồn và runtime của ứng dụng.
@@ -0,0 +1,25 @@
1
+ # Prompt dành cho Leader — triển khai Windows Computer Automation vào Herionix
2
+
3
+ Bạn là Leader kỹ thuật của dự án Herionix (desktop Windows). Hãy tích hợp Skill `windows-computer-automation` đính kèm vào hệ thống Bot/Teammate. Mục tiêu: mỗi bot được sử dụng có phân quyền cả Agent Desktop MCP và Windows-MCP; không có hai bot cùng tác động input trên một Windows interactive session.
4
+
5
+ ## Quy tắc làm việc
6
+
7
+ 1. Kiểm tra cấu trúc mã nguồn Herionix và cơ chế MCP/Tool/Skill hiện có; không đoán framework, đường dẫn hay version. Lập kế hoạch ngắn, phân task cho Teammate phù hợp, chỉ thay đổi các file cần thiết.
8
+ 2. Đọc `SKILL.md`, `references/herionix-integration.md`, `references/security.md`, `references/routing.md` trong gói Skill; dùng file `assets/mcp-servers.example.json` như cấu hình tham khảo, không giả định schema Herionix tương thích.
9
+ 3. Kết nối Agent Desktop (`agent-desktop mcp`) và Windows-MCP (`uvx windows-mcp serve` qua stdio). Cần kiểm tra `list_tools` động, healthcheck, startup/shutdown, timeout, lỗi transport; không spawn server trùng lặp cho từng bot dùng chung desktop.
10
+ 4. Xây các module `ToolRegistry`, `ComputerControlManager`, `AuthorizationService`, `DesktopSessionScheduler`, `ExecutionLedger`, `Verifier` hoặc tương đương với kiến trúc dự án.
11
+ 5. Ưu tiên Agent Desktop cho thao tác có ref accessibility; Windows-MCP cho App/FileSystem/Process và fallback UI. Quan sát trước thao tác, xác minh hậu điều kiện sau thao tác. Không retry mù sau side effect.
12
+ 6. Bảo vệ ở backend: allowlist đường dẫn đã canonicalize trên Windows, quyền tool từng bot, approval UI do người dùng xác nhận, disable PowerShell/Registry mặc định, khóa single-writer desktop, idempotency từng step, max 1 retry kỹ thuật + 2 vòng không tiến triển. Model không được tự gửi cờ phê duyệt để vượt policy.
13
+ 7. UI tiếng Việt trong `Tools > Windows Computer Automation`: bật/tắt providers, trạng thái kết nối, chọn thư mục cho phép, quyền Bot, hàng đợi đang giữ desktop, log tối giản, nút Dừng khẩn cấp.
14
+ 8. Bổ sung unit/integration tests cho đúng 8 kịch bản trong `references/herionix-integration.md`; thử nghiệm khi không có GUI session, khi MCP server crash, khi ref UI stale và khi người dùng điều khiển chuột.
15
+ 9. Không phá module Hermes, AionUI Teamate, các cấu hình Bot hoặc dữ liệu người dùng. Không expose MCP ra mạng công khai.
16
+
17
+ ## Tiêu chuẩn hoàn tất
18
+
19
+ - Có code thực thi trong Herionix, không chỉ prompt hoặc file SKILL.md.
20
+ - Bot có thể chọn provider theo nhiệm vụ, nhưng mọi desktop input được serialize.
21
+ - Không có quyền ngoài allowlist; thao tác rủi ro cần approval thật từ UI host.
22
+ - Unit + integration tests đạt, báo cáo file thay đổi và cách chạy.
23
+ - Không tuyên bố đã test GUI thật nếu chỉ chạy mock tests.
24
+
25
+ Hãy bắt đầu bằng việc khám phá codebase, trình bày ngắn những module hiện có và triển khai theo từng phần kiểm chứng được. Không hỏi lại những thông tin đã thể hiện trong mã nguồn.
@@ -0,0 +1,25 @@
1
+ {
2
+ "available": {
3
+ "agent_desktop": true,
4
+ "windows_mcp": true
5
+ },
6
+ "allowed_write_roots": [
7
+ "C:\\Users\\Public\\Documents\\Herionix-Exports"
8
+ ],
9
+ "system_tools_enabled": false,
10
+ "allow_executable_launch": false,
11
+ "max_retries_per_step": 1,
12
+ "max_no_progress_cycles": 2,
13
+ "require_host_confirmation_for": [
14
+ "delete",
15
+ "overwrite",
16
+ "send",
17
+ "upload",
18
+ "purchase",
19
+ "security_change",
20
+ "terminate_process"
21
+ ],
22
+ "allowed_read_roots": [
23
+ "C:\\Users\\Public\\Documents\\Herionix-Exports"
24
+ ]
25
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "mcpServers": {
3
+ "agent-desktop": {
4
+ "command": "agent-desktop",
5
+ "args": ["mcp"]
6
+ },
7
+ "windows-mcp": {
8
+ "command": "uvx",
9
+ "args": [
10
+ "windows-mcp",
11
+ "serve",
12
+ "--exclude-tools",
13
+ "PowerShell,Registry",
14
+ "--control-policy",
15
+ "balanced"
16
+ ]
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "id": "windows-computer-automation",
3
+ "name": "Windows Computer Automation",
4
+ "version": "1.0.0",
5
+ "description": "Điều khiển Windows an toàn: đọc màn hình, thao tác cửa sổ, nhập liệu, trình duyệt, ứng dụng, tệp và tiến trình qua Agent Desktop và Windows-MCP; chọn đúng công cụ, xác minh kết quả, chống lặp thao tác.",
6
+ "defaultEnabled": true,
7
+ "triggerTerms": [
8
+ "windows-computer-automation",
9
+ "điều khiển máy tính",
10
+ "điều khiển windows",
11
+ "thao tác trên máy",
12
+ "thao tác máy tính",
13
+ "tự động hóa windows",
14
+ "mở ứng dụng",
15
+ "chụp màn hình",
16
+ "computer use",
17
+ "windows-mcp",
18
+ "agent desktop",
19
+ "control my computer",
20
+ "automate windows"
21
+ ],
22
+ "requiredCapabilities": [],
23
+ "source": {
24
+ "repo": "leomieo (Limix AI)",
25
+ "license": "Proprietary (Limix AI)"
26
+ }
27
+ }
@@ -0,0 +1,72 @@
1
+ # Hướng dẫn tích hợp vào Herionix (Windows 10/11)
2
+
3
+ ## 1. Chuẩn bị công cụ
4
+
5
+ Cài Node.js/npm và Agent Desktop:
6
+
7
+ ```powershell
8
+ npm install -g agent-desktop
9
+ agent-desktop status
10
+ ```
11
+
12
+ Cài `uv` và Python phiên bản phù hợp theo trang Windows-MCP (README hiện nêu Python 3.14+). Với Windows-MCP, dùng lệnh sau để kiểm tra server và lưu ý nó sẽ tiếp tục chạy cho đến khi client/terminal đóng:
13
+
14
+ ```powershell
15
+ uvx windows-mcp serve --exclude-tools "PowerShell,Registry" --control-policy balanced
16
+ ```
17
+
18
+ Tài liệu nguồn:
19
+ - https://github.com/vercel-labs/agent-desktop
20
+ - https://github.com/CursorTouch/Windows-MCP
21
+
22
+ ## 2. Đăng ký hai MCP Server vào backend
23
+
24
+ Xem `assets/mcp-servers.example.json`. Đây là **MCP client config mẫu**, không phải định dạng config Herionix đã được xác nhận. Nếu Herionix dùng định dạng khác, chuyển hai `command` + `args` tương ứng vào cấu hình app. Chạy server trong phiên người dùng đang đăng nhập Windows và dùng stdio local.
25
+
26
+ Backend phải hỗ trợ startup/healthcheck, khám phá `list_tools`, chuẩn hóa lỗi, timeout, shutdown; không spawn bản sao MCP server cho từng bot nếu chúng cùng điều khiển một session. Nếu máy có nhiều interactive sessions, phân biệt bằng `desktop_id` thực tế.
27
+
28
+ ## 3. Gắn Skill
29
+
30
+ Đặt thư mục `windows-computer-automation` vào **skills directory mà Herionix thực sự tải**. Gắn Skill cho các Bot/Teammate được cho quyền computer-use. Nếu Herionix chưa có skill loader, cần thêm parser `SKILL.md` và entrypoints tương ứng — chỉ sao chép thư mục chưa biến thành kết nối MCP.
31
+
32
+ ## 4. Component đề xuất
33
+
34
+ ```text
35
+ Bot / Teammate
36
+ | task + intent
37
+ v
38
+ ComputerControlManager (orchestrator)
39
+ |-- policy + host approvals
40
+ |-- desktop mutex + task queue
41
+ |-- state snapshots + idempotency
42
+ |-- progress watchdog + verification
43
+ +-- AgentDesktopAdapter ------> agent-desktop MCP
44
+ +-- WindowsMcpAdapter --------> windows-mcp MCP
45
+ ```
46
+
47
+ Các module và trách nhiệm:
48
+ - `ToolRegistry`: khám phá tên, schema và trạng thái tool của hai server.
49
+ - `Planner/Router`: chọn provider theo tác vụ, không tự tạo lại tác dụng phụ khi chuyển provider.
50
+ - `AuthorizationService`: người dùng cấp quyền, allowlist path, risk classification và token một lần.
51
+ - `DesktopSessionScheduler`: khóa input per desktop/session và hàng đợi công bằng.
52
+ - `ExecutionLedger`: task ID, step ID, state digest, trạng thái hoàn thành, idempotency.
53
+ - `Verifier`: hậu điều kiện sau thao tác; phản hồi lỗi có phân loại để tránh vòng lặp.
54
+
55
+ ## 5. Cấu hình UI khuyến nghị
56
+
57
+ Trong `Herionix > Tools > Windows Computer Automation`: bật/tắt cả hai providers; ưu tiên Agent Desktop cho UI; hiển thị số bot đang chờ quyền desktop; cho phép chọn thư mục ghi; quyền admin tools mặc định off; số lần retry tối đa 1; giới hạn 2 vòng không tiến triển; nút Stop/Emergency Release; audit log đã che dữ liệu nhạy cảm.
58
+
59
+ ## 6. Kiểm thử chấp nhận
60
+
61
+ 1. Bot A nhấn nút trong Notepad bằng ref; Bot B chờ, không can thiệp bàn phím.
62
+ 2. Ref UI hết hạn → snapshot mới; không dùng lại ref cũ và không spam retry.
63
+ 3. Bot liệt kê file đọc-only; ghi `.txt` trong thư mục cho phép và xác minh file.
64
+ 4. Xóa file / chạy PowerShell / Registry bị block hoặc yêu cầu host approval tương ứng.
65
+ 5. MCP Server tắt giữa tác vụ → dừng có trạng thái rõ, không tự lặp thao tác.
66
+ 6. Cùng một `step_id` gửi hai lần → tác dụng phụ chỉ thực thi tối đa một lần ở host.
67
+ 7. No progress hai chu kỳ → dừng và báo lý do, không clear context để lặp vô hạn.
68
+ 8. Không có desktop session/unlock → báo bị chặn, không giả thành công.
69
+
70
+ ## 7. Phạm vi bản đóng gói
71
+
72
+ Skill này bao gồm quy trình, policy router có thể kiểm thử và cấu hình MCP mẫu. **Chưa phải bản cài đặt Herionix hoặc proxy MCP chạy sẵn**; thực thi connector, mutex liên tiến trình, duyệt quyền và xác minh file thật phải được hiện thực trong Herionix backend.