@xyagent/cli 0.0.1

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 (385) hide show
  1. package/README.md +860 -0
  2. package/bin/agentlink +8585 -0
  3. package/bin/agentlink-agent +7 -0
  4. package/bin/agentlink-mcp-stdio +173 -0
  5. package/docs/LOGGING.md +159 -0
  6. package/package.json +38 -0
  7. package/src/core.mjs +320 -0
  8. package/src/tunnel.mjs +291 -0
  9. package/src/tunnel_proxy.mjs +913 -0
  10. package/src/tunnel_service.mjs +699 -0
  11. package/src/tunnel_state.mjs +301 -0
  12. package/src/unified_producer.mjs +259 -0
  13. package/src-ext/README.md +75 -0
  14. package/src-ext/bin.mjs +128 -0
  15. package/src-ext/commands/agent.mjs +66 -0
  16. package/src-ext/commands/help.mjs +31 -0
  17. package/src-ext/commands/pair.mjs +455 -0
  18. package/src-ext/commands/reload.mjs +78 -0
  19. package/src-ext/commands/service.mjs +64 -0
  20. package/src-ext/core/accountResolver.mjs +137 -0
  21. package/src-ext/core/activeRuns.mjs +76 -0
  22. package/src-ext/core/agentlinkToolsBootstrap.mjs +75 -0
  23. package/src-ext/core/agentlinkToolsRegistry.mjs +323 -0
  24. package/src-ext/core/agentlinkToolsServer.mjs +109 -0
  25. package/src-ext/core/approvalGateway.mjs +132 -0
  26. package/src-ext/core/attachAuditLog.mjs +79 -0
  27. package/src-ext/core/attachDenylist.mjs +128 -0
  28. package/src-ext/core/autoTunnelDetector.mjs +98 -0
  29. package/src-ext/core/autoTunnelGateway.mjs +393 -0
  30. package/src-ext/core/autoTunnelWiring.mjs +184 -0
  31. package/src-ext/core/boundedMap.mjs +98 -0
  32. package/src-ext/core/bridgeAttachAutoScanner.mjs +237 -0
  33. package/src-ext/core/bridgeHooks.mjs +168 -0
  34. package/src-ext/core/bridgeSelfUninstall.mjs +133 -0
  35. package/src-ext/core/claudeTrust.mjs +74 -0
  36. package/src-ext/core/codexTrust.mjs +77 -0
  37. package/src-ext/core/cosUploadClient.mjs +326 -0
  38. package/src-ext/core/createWorkspace.mjs +170 -0
  39. package/src-ext/core/daemonRestart.mjs +49 -0
  40. package/src-ext/core/defaultWorkspace.mjs +65 -0
  41. package/src-ext/core/ensureGitRepo.mjs +57 -0
  42. package/src-ext/core/hermesProfile.mjs +75 -0
  43. package/src-ext/core/interruptGateway.mjs +119 -0
  44. package/src-ext/core/interruptRunListener.mjs +106 -0
  45. package/src-ext/core/mcpRuntimeFanout.mjs +236 -0
  46. package/src-ext/core/mcpStdioClient.mjs +316 -0
  47. package/src-ext/core/openclawGatewayClient.mjs +230 -0
  48. package/src-ext/core/pairCodeClient.mjs +331 -0
  49. package/src-ext/core/pairInventory.mjs +59 -0
  50. package/src-ext/core/pathExpand.mjs +34 -0
  51. package/src-ext/core/preprocessAttachments.mjs +113 -0
  52. package/src-ext/core/relayWorker.mjs +284 -0
  53. package/src-ext/core/relayWorkerHttp.mjs +58 -0
  54. package/src-ext/core/scanWorkspaces.mjs +842 -0
  55. package/src-ext/core/sessionSlot.mjs +70 -0
  56. package/src-ext/core/stateMachine.mjs +85 -0
  57. package/src-ext/core/toolsServerLifecycle.mjs +169 -0
  58. package/src-ext/core/unifiedDispatchHandler.mjs +439 -0
  59. package/src-ext/core/usageReporter.mjs +207 -0
  60. package/src-ext/openclaw-plugin/envelope-builder.cjs +619 -0
  61. package/src-ext/openclaw-plugin/index.cjs +1919 -0
  62. package/src-ext/openclaw-plugin/openclaw.plugin.json +20 -0
  63. package/src-ext/openclaw-plugin/package.json +12 -0
  64. package/src-ext/openclaw-plugin/relay-defaults.json +3 -0
  65. package/src-ext/openclaw-plugin/todoTranslatorUtils.cjs +101 -0
  66. package/src-ext/runtime/_shared/cliExec.mjs +138 -0
  67. package/src-ext/runtime/_shared/cosTextRefs.mjs +120 -0
  68. package/src-ext/runtime/_shared/fetchAndBase64Encode.mjs +52 -0
  69. package/src-ext/runtime/_shared/relayObjectToBlock.mjs +307 -0
  70. package/src-ext/runtime/_shared/sessionPicker.mjs +418 -0
  71. package/src-ext/runtime/_shared/slashCommandRouter.mjs +1346 -0
  72. package/src-ext/runtime/_shared/todoTranslatorUtils.mjs +94 -0
  73. package/src-ext/runtime/claude/askUserQuestionTranslator.mjs +241 -0
  74. package/src-ext/runtime/claude/customCommandsCollector.mjs +132 -0
  75. package/src-ext/runtime/claude/handleRequest.mjs +1418 -0
  76. package/src-ext/runtime/claude/index.mjs +160 -0
  77. package/src-ext/runtime/claude/internalCommandNames.mjs +58 -0
  78. package/src-ext/runtime/claude/launcher.mjs +313 -0
  79. package/src-ext/runtime/claude/mcpConfigAdapter.mjs +174 -0
  80. package/src-ext/runtime/claude/mcpConfigWriter.mjs +57 -0
  81. package/src-ext/runtime/claude/modelContextWindow.mjs +30 -0
  82. package/src-ext/runtime/claude/modelScanner.mjs +257 -0
  83. package/src-ext/runtime/claude/permissionInterruptBlock.mjs +86 -0
  84. package/src-ext/runtime/claude/permissionPromptServer.mjs +170 -0
  85. package/src-ext/runtime/claude/permissionPromptServerWorker.mjs +214 -0
  86. package/src-ext/runtime/claude/preflight.mjs +62 -0
  87. package/src-ext/runtime/claude/stdoutParser.mjs +365 -0
  88. package/src-ext/runtime/claude/todoTranslator.mjs +66 -0
  89. package/src-ext/runtime/claude/usageProbe.mjs +576 -0
  90. package/src-ext/runtime/claude/usageProbeStrip.mjs +14 -0
  91. package/src-ext/runtime/codex/codexCustomCommandsCollector.mjs +87 -0
  92. package/src-ext/runtime/codex/elicitationHandler.mjs +91 -0
  93. package/src-ext/runtime/codex/eventMapper.mjs +154 -0
  94. package/src-ext/runtime/codex/handleRequest.mjs +1152 -0
  95. package/src-ext/runtime/codex/index.mjs +142 -0
  96. package/src-ext/runtime/codex/mcpConfigAdapter.mjs +155 -0
  97. package/src-ext/runtime/codex/modelScanner.mjs +305 -0
  98. package/src-ext/runtime/codex/planNotificationHandler.mjs +61 -0
  99. package/src-ext/runtime/codex/preflight.mjs +67 -0
  100. package/src-ext/runtime/hermes/commandBackend.mjs +117 -0
  101. package/src-ext/runtime/hermes/envSetup.mjs +229 -0
  102. package/src-ext/runtime/hermes/gatewayManager.mjs +625 -0
  103. package/src-ext/runtime/hermes/handleRequest.mjs +1257 -0
  104. package/src-ext/runtime/hermes/httpBackend.mjs +219 -0
  105. package/src-ext/runtime/hermes/index.mjs +196 -0
  106. package/src-ext/runtime/hermes/mcpConfigAdapter.mjs +189 -0
  107. package/src-ext/runtime/hermes/preflight.mjs +359 -0
  108. package/src-ext/runtime/hermes/responsesBackend.mjs +276 -0
  109. package/src-ext/runtime/hermes/sessionStore.mjs +78 -0
  110. package/src-ext/runtime/hermes/todoTranslator.mjs +90 -0
  111. package/src-ext/runtime/hermes/workspaceRpc.mjs +124 -0
  112. package/src-ext/runtime/openclaw/buildOpenclawDaemonInput.mjs +143 -0
  113. package/src-ext/runtime/openclaw/mcpConfigAdapter.mjs +91 -0
  114. package/src-ext/runtime/openclaw/openclawConfig.mjs +145 -0
  115. package/src-ext/runtime/picoclaw/constants.mjs +39 -0
  116. package/src-ext/runtime/picoclaw/handleRequest.mjs +289 -0
  117. package/src-ext/runtime/picoclaw/index.mjs +314 -0
  118. package/src-ext/runtime/picoclaw/pairFlow.mjs +224 -0
  119. package/src-ext/runtime/picoclaw/state.mjs +78 -0
  120. package/src-ext/runtime/picoclaw/todoTranslator.mjs +67 -0
  121. package/src-ext/runtime/picoclaw/translator.mjs +272 -0
  122. package/src-ext/runtime/picoclaw/wsClient.mjs +129 -0
  123. package/src-ext/service/serviceManager.mjs +521 -0
  124. package/src-shared/README.md +44 -0
  125. package/src-shared/chat_push_normalizer.mjs +113 -0
  126. package/src-shared/correlation.mjs +25 -0
  127. package/src-shared/envelope_builder.mjs +612 -0
  128. package/src-shared/logger.mjs +167 -0
  129. package/src-shared/relay-defaults.json +3 -0
  130. package/src-shared/relay_revoke.mjs +46 -0
  131. package/src-shared/vendor/README.md +60 -0
  132. package/src-shared/vendor/smol-toml/LICENSE +24 -0
  133. package/src-shared/vendor/smol-toml/README.md +236 -0
  134. package/src-shared/vendor/smol-toml/VENDORED.md +37 -0
  135. package/src-shared/vendor/smol-toml/dist/date.d.ts +41 -0
  136. package/src-shared/vendor/smol-toml/dist/date.js +127 -0
  137. package/src-shared/vendor/smol-toml/dist/error.d.ts +38 -0
  138. package/src-shared/vendor/smol-toml/dist/error.js +63 -0
  139. package/src-shared/vendor/smol-toml/dist/extract.d.ts +30 -0
  140. package/src-shared/vendor/smol-toml/dist/extract.js +109 -0
  141. package/src-shared/vendor/smol-toml/dist/index.cjs +900 -0
  142. package/src-shared/vendor/smol-toml/dist/index.d.ts +43 -0
  143. package/src-shared/vendor/smol-toml/dist/index.js +33 -0
  144. package/src-shared/vendor/smol-toml/dist/parse.d.ts +37 -0
  145. package/src-shared/vendor/smol-toml/dist/parse.js +148 -0
  146. package/src-shared/vendor/smol-toml/dist/primitive.d.ts +31 -0
  147. package/src-shared/vendor/smol-toml/dist/primitive.js +178 -0
  148. package/src-shared/vendor/smol-toml/dist/stringify.d.ts +31 -0
  149. package/src-shared/vendor/smol-toml/dist/stringify.js +163 -0
  150. package/src-shared/vendor/smol-toml/dist/struct.d.ts +32 -0
  151. package/src-shared/vendor/smol-toml/dist/struct.js +194 -0
  152. package/src-shared/vendor/smol-toml/dist/util.d.ts +42 -0
  153. package/src-shared/vendor/smol-toml/dist/util.js +100 -0
  154. package/src-shared/vendor/smol-toml/package.json +54 -0
  155. package/src-shared/vendor/yaml/LICENSE +13 -0
  156. package/src-shared/vendor/yaml/README.md +155 -0
  157. package/src-shared/vendor/yaml/VENDORED.md +29 -0
  158. package/src-shared/vendor/yaml/bin.mjs +11 -0
  159. package/src-shared/vendor/yaml/browser/dist/compose/compose-collection.js +88 -0
  160. package/src-shared/vendor/yaml/browser/dist/compose/compose-doc.js +42 -0
  161. package/src-shared/vendor/yaml/browser/dist/compose/compose-node.js +92 -0
  162. package/src-shared/vendor/yaml/browser/dist/compose/compose-scalar.js +80 -0
  163. package/src-shared/vendor/yaml/browser/dist/compose/composer.js +217 -0
  164. package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-map.js +113 -0
  165. package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-scalar.js +198 -0
  166. package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-seq.js +47 -0
  167. package/src-shared/vendor/yaml/browser/dist/compose/resolve-end.js +37 -0
  168. package/src-shared/vendor/yaml/browser/dist/compose/resolve-flow-collection.js +203 -0
  169. package/src-shared/vendor/yaml/browser/dist/compose/resolve-flow-scalar.js +223 -0
  170. package/src-shared/vendor/yaml/browser/dist/compose/resolve-props.js +148 -0
  171. package/src-shared/vendor/yaml/browser/dist/compose/util-contains-newline.js +34 -0
  172. package/src-shared/vendor/yaml/browser/dist/compose/util-empty-scalar-position.js +27 -0
  173. package/src-shared/vendor/yaml/browser/dist/compose/util-flow-indent-check.js +15 -0
  174. package/src-shared/vendor/yaml/browser/dist/compose/util-map-includes.js +17 -0
  175. package/src-shared/vendor/yaml/browser/dist/doc/Document.js +334 -0
  176. package/src-shared/vendor/yaml/browser/dist/doc/anchors.js +72 -0
  177. package/src-shared/vendor/yaml/browser/dist/doc/applyReviver.js +55 -0
  178. package/src-shared/vendor/yaml/browser/dist/doc/createNode.js +89 -0
  179. package/src-shared/vendor/yaml/browser/dist/doc/directives.js +176 -0
  180. package/src-shared/vendor/yaml/browser/dist/errors.js +57 -0
  181. package/src-shared/vendor/yaml/browser/dist/index.js +17 -0
  182. package/src-shared/vendor/yaml/browser/dist/log.js +14 -0
  183. package/src-shared/vendor/yaml/browser/dist/nodes/Alias.js +101 -0
  184. package/src-shared/vendor/yaml/browser/dist/nodes/Collection.js +147 -0
  185. package/src-shared/vendor/yaml/browser/dist/nodes/Node.js +38 -0
  186. package/src-shared/vendor/yaml/browser/dist/nodes/Pair.js +36 -0
  187. package/src-shared/vendor/yaml/browser/dist/nodes/Scalar.js +24 -0
  188. package/src-shared/vendor/yaml/browser/dist/nodes/YAMLMap.js +144 -0
  189. package/src-shared/vendor/yaml/browser/dist/nodes/YAMLSeq.js +113 -0
  190. package/src-shared/vendor/yaml/browser/dist/nodes/addPairToJSMap.js +104 -0
  191. package/src-shared/vendor/yaml/browser/dist/nodes/identity.js +36 -0
  192. package/src-shared/vendor/yaml/browser/dist/nodes/toJS.js +37 -0
  193. package/src-shared/vendor/yaml/browser/dist/parse/cst-scalar.js +214 -0
  194. package/src-shared/vendor/yaml/browser/dist/parse/cst-stringify.js +61 -0
  195. package/src-shared/vendor/yaml/browser/dist/parse/cst-visit.js +97 -0
  196. package/src-shared/vendor/yaml/browser/dist/parse/cst.js +98 -0
  197. package/src-shared/vendor/yaml/browser/dist/parse/lexer.js +717 -0
  198. package/src-shared/vendor/yaml/browser/dist/parse/line-counter.js +39 -0
  199. package/src-shared/vendor/yaml/browser/dist/parse/parser.js +954 -0
  200. package/src-shared/vendor/yaml/browser/dist/public-api.js +99 -0
  201. package/src-shared/vendor/yaml/browser/dist/schema/Schema.js +38 -0
  202. package/src-shared/vendor/yaml/browser/dist/schema/common/map.js +17 -0
  203. package/src-shared/vendor/yaml/browser/dist/schema/common/null.js +15 -0
  204. package/src-shared/vendor/yaml/browser/dist/schema/common/seq.js +17 -0
  205. package/src-shared/vendor/yaml/browser/dist/schema/common/string.js +14 -0
  206. package/src-shared/vendor/yaml/browser/dist/schema/core/bool.js +19 -0
  207. package/src-shared/vendor/yaml/browser/dist/schema/core/float.js +43 -0
  208. package/src-shared/vendor/yaml/browser/dist/schema/core/int.js +38 -0
  209. package/src-shared/vendor/yaml/browser/dist/schema/core/schema.js +23 -0
  210. package/src-shared/vendor/yaml/browser/dist/schema/json/schema.js +62 -0
  211. package/src-shared/vendor/yaml/browser/dist/schema/tags.js +83 -0
  212. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/binary.js +66 -0
  213. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/bool.js +26 -0
  214. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/float.js +46 -0
  215. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/int.js +71 -0
  216. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/omap.js +74 -0
  217. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/pairs.js +78 -0
  218. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/schema.js +37 -0
  219. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/set.js +93 -0
  220. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/timestamp.js +101 -0
  221. package/src-shared/vendor/yaml/browser/dist/stringify/foldFlowLines.js +146 -0
  222. package/src-shared/vendor/yaml/browser/dist/stringify/stringify.js +124 -0
  223. package/src-shared/vendor/yaml/browser/dist/stringify/stringifyCollection.js +143 -0
  224. package/src-shared/vendor/yaml/browser/dist/stringify/stringifyComment.js +20 -0
  225. package/src-shared/vendor/yaml/browser/dist/stringify/stringifyDocument.js +85 -0
  226. package/src-shared/vendor/yaml/browser/dist/stringify/stringifyNumber.js +24 -0
  227. package/src-shared/vendor/yaml/browser/dist/stringify/stringifyPair.js +150 -0
  228. package/src-shared/vendor/yaml/browser/dist/stringify/stringifyString.js +328 -0
  229. package/src-shared/vendor/yaml/browser/dist/util.js +11 -0
  230. package/src-shared/vendor/yaml/browser/dist/visit.js +233 -0
  231. package/src-shared/vendor/yaml/browser/index.js +5 -0
  232. package/src-shared/vendor/yaml/browser/package.json +3 -0
  233. package/src-shared/vendor/yaml/dist/cli.d.ts +8 -0
  234. package/src-shared/vendor/yaml/dist/cli.mjs +199 -0
  235. package/src-shared/vendor/yaml/dist/compose/compose-collection.d.ts +11 -0
  236. package/src-shared/vendor/yaml/dist/compose/compose-collection.js +90 -0
  237. package/src-shared/vendor/yaml/dist/compose/compose-doc.d.ts +7 -0
  238. package/src-shared/vendor/yaml/dist/compose/compose-doc.js +44 -0
  239. package/src-shared/vendor/yaml/dist/compose/compose-node.d.ts +28 -0
  240. package/src-shared/vendor/yaml/dist/compose/compose-node.js +95 -0
  241. package/src-shared/vendor/yaml/dist/compose/compose-scalar.d.ts +5 -0
  242. package/src-shared/vendor/yaml/dist/compose/compose-scalar.js +82 -0
  243. package/src-shared/vendor/yaml/dist/compose/composer.d.ts +62 -0
  244. package/src-shared/vendor/yaml/dist/compose/composer.js +221 -0
  245. package/src-shared/vendor/yaml/dist/compose/resolve-block-map.d.ts +6 -0
  246. package/src-shared/vendor/yaml/dist/compose/resolve-block-map.js +115 -0
  247. package/src-shared/vendor/yaml/dist/compose/resolve-block-scalar.d.ts +11 -0
  248. package/src-shared/vendor/yaml/dist/compose/resolve-block-scalar.js +200 -0
  249. package/src-shared/vendor/yaml/dist/compose/resolve-block-seq.d.ts +6 -0
  250. package/src-shared/vendor/yaml/dist/compose/resolve-block-seq.js +49 -0
  251. package/src-shared/vendor/yaml/dist/compose/resolve-end.d.ts +6 -0
  252. package/src-shared/vendor/yaml/dist/compose/resolve-end.js +39 -0
  253. package/src-shared/vendor/yaml/dist/compose/resolve-flow-collection.d.ts +7 -0
  254. package/src-shared/vendor/yaml/dist/compose/resolve-flow-collection.js +205 -0
  255. package/src-shared/vendor/yaml/dist/compose/resolve-flow-scalar.d.ts +10 -0
  256. package/src-shared/vendor/yaml/dist/compose/resolve-flow-scalar.js +225 -0
  257. package/src-shared/vendor/yaml/dist/compose/resolve-props.d.ts +23 -0
  258. package/src-shared/vendor/yaml/dist/compose/resolve-props.js +150 -0
  259. package/src-shared/vendor/yaml/dist/compose/util-contains-newline.d.ts +2 -0
  260. package/src-shared/vendor/yaml/dist/compose/util-contains-newline.js +36 -0
  261. package/src-shared/vendor/yaml/dist/compose/util-empty-scalar-position.d.ts +2 -0
  262. package/src-shared/vendor/yaml/dist/compose/util-empty-scalar-position.js +29 -0
  263. package/src-shared/vendor/yaml/dist/compose/util-flow-indent-check.d.ts +3 -0
  264. package/src-shared/vendor/yaml/dist/compose/util-flow-indent-check.js +17 -0
  265. package/src-shared/vendor/yaml/dist/compose/util-map-includes.d.ts +4 -0
  266. package/src-shared/vendor/yaml/dist/compose/util-map-includes.js +19 -0
  267. package/src-shared/vendor/yaml/dist/doc/Document.d.ts +141 -0
  268. package/src-shared/vendor/yaml/dist/doc/Document.js +336 -0
  269. package/src-shared/vendor/yaml/dist/doc/anchors.d.ts +24 -0
  270. package/src-shared/vendor/yaml/dist/doc/anchors.js +77 -0
  271. package/src-shared/vendor/yaml/dist/doc/applyReviver.d.ts +9 -0
  272. package/src-shared/vendor/yaml/dist/doc/applyReviver.js +57 -0
  273. package/src-shared/vendor/yaml/dist/doc/createNode.d.ts +17 -0
  274. package/src-shared/vendor/yaml/dist/doc/createNode.js +91 -0
  275. package/src-shared/vendor/yaml/dist/doc/directives.d.ts +49 -0
  276. package/src-shared/vendor/yaml/dist/doc/directives.js +178 -0
  277. package/src-shared/vendor/yaml/dist/errors.d.ts +21 -0
  278. package/src-shared/vendor/yaml/dist/errors.js +62 -0
  279. package/src-shared/vendor/yaml/dist/index.d.ts +22 -0
  280. package/src-shared/vendor/yaml/dist/index.js +50 -0
  281. package/src-shared/vendor/yaml/dist/log.d.ts +3 -0
  282. package/src-shared/vendor/yaml/dist/log.js +17 -0
  283. package/src-shared/vendor/yaml/dist/nodes/Alias.d.ts +28 -0
  284. package/src-shared/vendor/yaml/dist/nodes/Alias.js +103 -0
  285. package/src-shared/vendor/yaml/dist/nodes/Collection.d.ts +73 -0
  286. package/src-shared/vendor/yaml/dist/nodes/Collection.js +151 -0
  287. package/src-shared/vendor/yaml/dist/nodes/Node.d.ts +47 -0
  288. package/src-shared/vendor/yaml/dist/nodes/Node.js +40 -0
  289. package/src-shared/vendor/yaml/dist/nodes/Pair.d.ts +21 -0
  290. package/src-shared/vendor/yaml/dist/nodes/Pair.js +39 -0
  291. package/src-shared/vendor/yaml/dist/nodes/Scalar.d.ts +42 -0
  292. package/src-shared/vendor/yaml/dist/nodes/Scalar.js +27 -0
  293. package/src-shared/vendor/yaml/dist/nodes/YAMLMap.d.ts +53 -0
  294. package/src-shared/vendor/yaml/dist/nodes/YAMLMap.js +147 -0
  295. package/src-shared/vendor/yaml/dist/nodes/YAMLSeq.d.ts +60 -0
  296. package/src-shared/vendor/yaml/dist/nodes/YAMLSeq.js +115 -0
  297. package/src-shared/vendor/yaml/dist/nodes/addPairToJSMap.d.ts +4 -0
  298. package/src-shared/vendor/yaml/dist/nodes/addPairToJSMap.js +106 -0
  299. package/src-shared/vendor/yaml/dist/nodes/identity.d.ts +23 -0
  300. package/src-shared/vendor/yaml/dist/nodes/identity.js +53 -0
  301. package/src-shared/vendor/yaml/dist/nodes/toJS.d.ts +27 -0
  302. package/src-shared/vendor/yaml/dist/nodes/toJS.js +39 -0
  303. package/src-shared/vendor/yaml/dist/options.d.ts +338 -0
  304. package/src-shared/vendor/yaml/dist/parse/cst-scalar.d.ts +64 -0
  305. package/src-shared/vendor/yaml/dist/parse/cst-scalar.js +218 -0
  306. package/src-shared/vendor/yaml/dist/parse/cst-stringify.d.ts +8 -0
  307. package/src-shared/vendor/yaml/dist/parse/cst-stringify.js +63 -0
  308. package/src-shared/vendor/yaml/dist/parse/cst-visit.d.ts +39 -0
  309. package/src-shared/vendor/yaml/dist/parse/cst-visit.js +99 -0
  310. package/src-shared/vendor/yaml/dist/parse/cst.d.ts +108 -0
  311. package/src-shared/vendor/yaml/dist/parse/cst.js +112 -0
  312. package/src-shared/vendor/yaml/dist/parse/lexer.d.ts +87 -0
  313. package/src-shared/vendor/yaml/dist/parse/lexer.js +719 -0
  314. package/src-shared/vendor/yaml/dist/parse/line-counter.d.ts +22 -0
  315. package/src-shared/vendor/yaml/dist/parse/line-counter.js +41 -0
  316. package/src-shared/vendor/yaml/dist/parse/parser.d.ts +84 -0
  317. package/src-shared/vendor/yaml/dist/parse/parser.js +958 -0
  318. package/src-shared/vendor/yaml/dist/public-api.d.ts +43 -0
  319. package/src-shared/vendor/yaml/dist/public-api.js +104 -0
  320. package/src-shared/vendor/yaml/dist/schema/Schema.d.ts +18 -0
  321. package/src-shared/vendor/yaml/dist/schema/Schema.js +40 -0
  322. package/src-shared/vendor/yaml/dist/schema/common/map.d.ts +2 -0
  323. package/src-shared/vendor/yaml/dist/schema/common/map.js +19 -0
  324. package/src-shared/vendor/yaml/dist/schema/common/null.d.ts +4 -0
  325. package/src-shared/vendor/yaml/dist/schema/common/null.js +17 -0
  326. package/src-shared/vendor/yaml/dist/schema/common/seq.d.ts +2 -0
  327. package/src-shared/vendor/yaml/dist/schema/common/seq.js +19 -0
  328. package/src-shared/vendor/yaml/dist/schema/common/string.d.ts +2 -0
  329. package/src-shared/vendor/yaml/dist/schema/common/string.js +16 -0
  330. package/src-shared/vendor/yaml/dist/schema/core/bool.d.ts +4 -0
  331. package/src-shared/vendor/yaml/dist/schema/core/bool.js +21 -0
  332. package/src-shared/vendor/yaml/dist/schema/core/float.d.ts +4 -0
  333. package/src-shared/vendor/yaml/dist/schema/core/float.js +47 -0
  334. package/src-shared/vendor/yaml/dist/schema/core/int.d.ts +4 -0
  335. package/src-shared/vendor/yaml/dist/schema/core/int.js +42 -0
  336. package/src-shared/vendor/yaml/dist/schema/core/schema.d.ts +1 -0
  337. package/src-shared/vendor/yaml/dist/schema/core/schema.js +25 -0
  338. package/src-shared/vendor/yaml/dist/schema/json/schema.d.ts +2 -0
  339. package/src-shared/vendor/yaml/dist/schema/json/schema.js +64 -0
  340. package/src-shared/vendor/yaml/dist/schema/json-schema.d.ts +69 -0
  341. package/src-shared/vendor/yaml/dist/schema/tags.d.ts +40 -0
  342. package/src-shared/vendor/yaml/dist/schema/tags.js +86 -0
  343. package/src-shared/vendor/yaml/dist/schema/types.d.ts +90 -0
  344. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/binary.d.ts +2 -0
  345. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/binary.js +68 -0
  346. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/bool.d.ts +7 -0
  347. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/bool.js +29 -0
  348. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/float.d.ts +4 -0
  349. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/float.js +50 -0
  350. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/int.d.ts +5 -0
  351. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/int.js +76 -0
  352. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/omap.d.ts +28 -0
  353. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/omap.js +77 -0
  354. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/pairs.d.ts +10 -0
  355. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/pairs.js +82 -0
  356. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/schema.d.ts +1 -0
  357. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/schema.js +39 -0
  358. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/set.d.ts +28 -0
  359. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/set.js +96 -0
  360. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/timestamp.d.ts +6 -0
  361. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/timestamp.js +105 -0
  362. package/src-shared/vendor/yaml/dist/stringify/foldFlowLines.d.ts +34 -0
  363. package/src-shared/vendor/yaml/dist/stringify/foldFlowLines.js +151 -0
  364. package/src-shared/vendor/yaml/dist/stringify/stringify.d.ts +21 -0
  365. package/src-shared/vendor/yaml/dist/stringify/stringify.js +127 -0
  366. package/src-shared/vendor/yaml/dist/stringify/stringifyCollection.d.ts +17 -0
  367. package/src-shared/vendor/yaml/dist/stringify/stringifyCollection.js +145 -0
  368. package/src-shared/vendor/yaml/dist/stringify/stringifyComment.d.ts +10 -0
  369. package/src-shared/vendor/yaml/dist/stringify/stringifyComment.js +24 -0
  370. package/src-shared/vendor/yaml/dist/stringify/stringifyDocument.d.ts +4 -0
  371. package/src-shared/vendor/yaml/dist/stringify/stringifyDocument.js +87 -0
  372. package/src-shared/vendor/yaml/dist/stringify/stringifyNumber.d.ts +2 -0
  373. package/src-shared/vendor/yaml/dist/stringify/stringifyNumber.js +26 -0
  374. package/src-shared/vendor/yaml/dist/stringify/stringifyPair.d.ts +3 -0
  375. package/src-shared/vendor/yaml/dist/stringify/stringifyPair.js +152 -0
  376. package/src-shared/vendor/yaml/dist/stringify/stringifyString.d.ts +9 -0
  377. package/src-shared/vendor/yaml/dist/stringify/stringifyString.js +330 -0
  378. package/src-shared/vendor/yaml/dist/test-events.d.ts +4 -0
  379. package/src-shared/vendor/yaml/dist/test-events.js +134 -0
  380. package/src-shared/vendor/yaml/dist/util.d.ts +12 -0
  381. package/src-shared/vendor/yaml/dist/util.js +28 -0
  382. package/src-shared/vendor/yaml/dist/visit.d.ts +102 -0
  383. package/src-shared/vendor/yaml/dist/visit.js +236 -0
  384. package/src-shared/vendor/yaml/package.json +96 -0
  385. package/src-shared/vendor/yaml/util.js +2 -0
@@ -0,0 +1,78 @@
1
+ // Hermes 会话历史持久化。最简 JSON 文件 store —— 跟 Python 版的语义一致:
2
+ // - 每个 sessionId 一份 conversation history({role, content}[])
3
+ // - 持久化到 ~/.agentlink/hermes/sessions/<sessionId>.json
4
+ // - 提供 append / read / clear / list 四个原语
5
+ //
6
+ // 没有 token 计数 —— 上层 OpenAI-compatible API 自带 usage 字段,bridge 只需
7
+ // 转发即可。
8
+
9
+ import { promises as fsp } from "node:fs";
10
+ import path from "node:path";
11
+ import os from "node:os";
12
+
13
+ const ROOT = path.join(os.homedir(), ".agentlink", "hermes", "sessions");
14
+ const MAX_HISTORY_TURNS = 80; // hard ceiling — bridge is not a context manager
15
+
16
+ export async function ensureRoot() {
17
+ await fsp.mkdir(ROOT, { recursive: true });
18
+ }
19
+
20
+ function pathOf(sessionId) {
21
+ // 防 path traversal — 限定 alnum + - + _
22
+ const safe = String(sessionId || "").replace(/[^a-zA-Z0-9._\-]/g, "_");
23
+ return path.join(ROOT, `${safe}.json`);
24
+ }
25
+
26
+ /** Returns array of {role, content} (oldest first). Missing → []. */
27
+ export async function readHistory(sessionId) {
28
+ try {
29
+ const buf = await fsp.readFile(pathOf(sessionId), "utf8");
30
+ const data = JSON.parse(buf);
31
+ if (Array.isArray(data?.messages)) return data.messages;
32
+ return [];
33
+ } catch (err) {
34
+ if (err && /** @type {any} */ (err).code === "ENOENT") return [];
35
+ throw err;
36
+ }
37
+ }
38
+
39
+ /** Append one user/assistant turn. Truncates to MAX_HISTORY_TURNS pairs. */
40
+ export async function appendTurn(sessionId, userText, assistantText) {
41
+ const cur = await readHistory(sessionId);
42
+ const next = cur.slice();
43
+ if (userText && userText.trim()) {
44
+ next.push({ role: "user", content: userText });
45
+ }
46
+ if (assistantText && assistantText.trim()) {
47
+ next.push({ role: "assistant", content: assistantText });
48
+ }
49
+ // Trim oldest pairs first to keep within MAX_HISTORY_TURNS *messages*.
50
+ const trimmed = next.slice(Math.max(0, next.length - MAX_HISTORY_TURNS * 2));
51
+ await ensureRoot();
52
+ await fsp.writeFile(
53
+ pathOf(sessionId),
54
+ JSON.stringify({ messages: trimmed }, null, 2),
55
+ "utf8",
56
+ );
57
+ }
58
+
59
+ export async function clearHistory(sessionId) {
60
+ try {
61
+ await fsp.unlink(pathOf(sessionId));
62
+ } catch (err) {
63
+ if (err && /** @type {any} */ (err).code !== "ENOENT") throw err;
64
+ }
65
+ }
66
+
67
+ /** Returns array of session ids (basename without .json). */
68
+ export async function listSessions() {
69
+ try {
70
+ const entries = await fsp.readdir(ROOT);
71
+ return entries
72
+ .filter((n) => n.endsWith(".json"))
73
+ .map((n) => n.slice(0, -".json".length));
74
+ } catch (err) {
75
+ if (err && /** @type {any} */ (err).code === "ENOENT") return [];
76
+ throw err;
77
+ }
78
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * todoTranslator.mjs — Hermes runtime todo → todo_list block translator
3
+ *
4
+ * 按 translator-spec.md 和 npm-cli-todo-translator-hermes spec 实现:
5
+ * - 识别 kind==="tool_call_started" && name==="todo"
6
+ * - 支持 merge=true 增量合并(内部维护 lastItems 状态)
7
+ * - 转写为 content_block.started + content_block.completed
8
+ * - tool_call_completed(name=todo) 静默丢弃
9
+ * - 其他 chunk 原样透传
10
+ */
11
+
12
+ import {
13
+ buildBlockStarted,
14
+ buildBlockCompleted,
15
+ newBlockId,
16
+ } from "../../../src-shared/envelope_builder.mjs";
17
+ import { buildTodoItems, normalizeStatus } from "../_shared/todoTranslatorUtils.mjs";
18
+
19
+ /**
20
+ * 创建 Hermes todo translator 实例。
21
+ *
22
+ * @param {string} runId 当前 run 的 run_id,用于构造 envelope
23
+ * @returns {{ intercept(chunk: object): object[] }}
24
+ */
25
+ export function createHermesTodoTranslator(runId) {
26
+ // 内部维护 lastItems 状态(用于 merge=true 增量合并)
27
+ let lastItems = [];
28
+
29
+ return {
30
+ /**
31
+ * @param {object} chunk Responses API 路径的 chunk 对象
32
+ * @returns {object[]} 0~2 个 envelope 或原始 chunk
33
+ */
34
+ intercept(chunk) {
35
+ if (!chunk || typeof chunk !== "object") return [chunk];
36
+
37
+ // 识别 todo tool_call_started
38
+ if (chunk.kind === "tool_call_started" && chunk.name === "todo") {
39
+ let parsed = {};
40
+ if (chunk.arguments) {
41
+ try { parsed = JSON.parse(String(chunk.arguments)); } catch { /* 保持空对象 */ }
42
+ }
43
+
44
+ const rawTodos = Array.isArray(parsed.todos) ? parsed.todos : [];
45
+ const merge = parsed.merge === true;
46
+
47
+ // 构造本次 rawItems(id 原值保留,status 4 态含 cancelled)
48
+ const rawItems = rawTodos.map((item, idx) => {
49
+ const id = typeof item?.id === "string" && item.id ? item.id : `td_${idx}`;
50
+ return { ...item, id };
51
+ });
52
+ const currentItems = buildTodoItems(rawItems);
53
+
54
+ if (merge) {
55
+ // merge=true:upsert 到 lastItems
56
+ const merged = [...lastItems];
57
+ for (const newItem of currentItems) {
58
+ const existingIdx = merged.findIndex((x) => x.id === newItem.id);
59
+ if (existingIdx >= 0) {
60
+ merged[existingIdx] = { ...merged[existingIdx], ...newItem };
61
+ } else {
62
+ merged.push(newItem);
63
+ }
64
+ }
65
+ // 重新赋 order(按最终数组位置)
66
+ lastItems = merged.map((item, idx) => ({ ...item, order: idx }));
67
+ } else {
68
+ // merge=false(默认):全量替换
69
+ lastItems = currentItems;
70
+ }
71
+
72
+ const blockId = newBlockId();
73
+ const block = { id: blockId, type: "todo_list", items: lastItems };
74
+
75
+ return [
76
+ buildBlockStarted(runId, block),
77
+ buildBlockCompleted(runId, { ...block }),
78
+ ];
79
+ }
80
+
81
+ // 静默丢弃 todo tool_call_completed
82
+ if (chunk.kind === "tool_call_completed" && chunk.name === "todo") {
83
+ return [];
84
+ }
85
+
86
+ // 其他 chunk 原样透传
87
+ return [chunk];
88
+ },
89
+ };
90
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * workspaceRpc.mjs — hermes bridge workspace RPC handler
3
+ *
4
+ * 处理以下 RPC:
5
+ * - identity.soul.set : 写 <hermesHome>/SOUL.md(用户在 App "AI 身份 sheet"
6
+ * 提交新名字 / type / style 后,relay fan-out 到这里把 SOUL.md 写入磁盘)。
7
+ * - memory.files.get : 读 <hermesHome>/SOUL.md(App 提交完身份后调
8
+ * `refreshLinkedOpenClawIdentity` 拉最新内容刷标题缓存)。
9
+ *
10
+ * 设计约束:
11
+ * - 路径硬编码为 SOUL.md,name=SOUL.md 之外的请求都按 missing 返回
12
+ * (hermes 没有 IDENTITY.md / memories/ 子目录的概念),防止误扩 RPC 白名单
13
+ * - 写入失败返回 { ok: false, error } 而非抛异常,让 handleRequest 统一上报
14
+ *
15
+ * @module workspaceRpc
16
+ */
17
+
18
+ import fs from "node:fs/promises";
19
+ import os from "node:os";
20
+ import path from "node:path";
21
+
22
+ /**
23
+ * @typedef {{ ok: true } | { ok: false, error: string }} RpcResult
24
+ */
25
+
26
+ /**
27
+ * createHermesWorkspaceRpc 工厂函数。
28
+ *
29
+ * @param {object} opts
30
+ * @param {string} [opts.hermesHome] hermes 根目录。默认 process.env.HERMES_HOME ?? ~/.hermes
31
+ * @param {{ info, warn, error }} opts.log logger
32
+ * @returns {{ dispatch(method: string, params: object): Promise<RpcResult> }}
33
+ */
34
+ export function createHermesWorkspaceRpc({ hermesHome, log } = {}) {
35
+ const home = hermesHome ?? process.env.HERMES_HOME ?? path.join(os.homedir(), ".hermes");
36
+ const SOUL_FILE = "SOUL.md";
37
+ const soulPath = path.join(home, SOUL_FILE);
38
+
39
+ /**
40
+ * @param {string} method
41
+ * @param {object} params
42
+ * @returns {Promise<RpcResult>}
43
+ */
44
+ async function dispatch(method, params = {}) {
45
+ if (method === "identity.soul.set") {
46
+ return writeSoulMd(params);
47
+ }
48
+ if (method === "memory.files.get") {
49
+ return readSoulMd(params);
50
+ }
51
+
52
+ log.warn("workspaceRpc.unsupported", `unsupported rpc method: ${method}`, { method });
53
+ return { ok: false, error: `unsupported rpc method: ${method}` };
54
+ }
55
+
56
+ /**
57
+ * 写入 <hermesHome>/SOUL.md。
58
+ * params.name 字段被忽略(路径硬编码)。
59
+ */
60
+ async function writeSoulMd(params) {
61
+ const content = String(params?.content ?? "");
62
+ try {
63
+ await fs.mkdir(home, { recursive: true });
64
+ await fs.writeFile(soulPath, content, "utf-8");
65
+ log.info("workspaceRpc.soul.written", "SOUL.md written", { path: soulPath, bytes: content.length });
66
+ return { ok: true };
67
+ } catch (err) {
68
+ const msg = err?.message ?? String(err);
69
+ log.error("workspaceRpc.soul.write.failed", "SOUL.md write failed", { path: soulPath, error: msg });
70
+ return { ok: false, error: msg };
71
+ }
72
+ }
73
+
74
+ /**
75
+ * 读 <hermesHome>/SOUL.md。
76
+ * 返回 OpenClawWorkspaceApi 期望的 { ok:true, file: {name,path,missing,size,updatedAtMs,content} } 形态。
77
+ * 文件不存在不报错——返回 missing:true,让 App 端走"未填写身份"分支。
78
+ *
79
+ * params.name 必须是 "SOUL.md"(大小写无关)。其它 name(IDENTITY.md / memories/...)
80
+ * 在 hermes 这里都不存在,统一按 missing 返回 —— 比 unsupported 友好,避免 App
81
+ * 误以为 RPC 完全坏掉。
82
+ */
83
+ async function readSoulMd(params) {
84
+ const requested = String(params?.name ?? "").trim();
85
+ if (requested.toUpperCase() !== SOUL_FILE.toUpperCase()) {
86
+ log.info(
87
+ "workspaceRpc.memory.skip",
88
+ "hermes only owns SOUL.md, other names report missing",
89
+ { requested },
90
+ );
91
+ return {
92
+ ok: true,
93
+ file: { name: requested || SOUL_FILE, path: "", missing: true, size: 0, updatedAtMs: 0, content: "" },
94
+ };
95
+ }
96
+ try {
97
+ const stat = await fs.stat(soulPath).catch(() => null);
98
+ if (!stat) {
99
+ return {
100
+ ok: true,
101
+ file: { name: SOUL_FILE, path: soulPath, missing: true, size: 0, updatedAtMs: 0, content: "" },
102
+ };
103
+ }
104
+ const content = await fs.readFile(soulPath, "utf-8");
105
+ return {
106
+ ok: true,
107
+ file: {
108
+ name: SOUL_FILE,
109
+ path: soulPath,
110
+ missing: false,
111
+ size: stat.size,
112
+ updatedAtMs: Math.floor(stat.mtimeMs),
113
+ content,
114
+ },
115
+ };
116
+ } catch (err) {
117
+ const msg = err?.message ?? String(err);
118
+ log.error("workspaceRpc.soul.read.failed", "SOUL.md read failed", { path: soulPath, error: msg });
119
+ return { ok: false, error: msg };
120
+ }
121
+ }
122
+
123
+ return { dispatch };
124
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * buildOpenclawDaemonInput.mjs — openclaw daemon `/v1/responses` input 构造器
3
+ *
4
+ * openclaw daemon 使用自定义 multimodal schema(非 OpenAI Responses 官方标准):
5
+ * - 纯文本时:input 为字符串(向后兼容,不改变现有路径)
6
+ * - 多模态时:input 为 [{type:"message", role:"user", content:[...]}]
7
+ *
8
+ * content item 类型:
9
+ * - {type:"input_text", text}
10
+ * - {type:"input_image", source:{type:"base64", media_type, data:<纯base64>}}
11
+ * - {type:"input_file", source:{type:"base64", media_type:"application/pdf", filename, data:<纯base64>}}
12
+ *
13
+ * base64 编码要求:纯 base64 字符串,无 data: URI 前缀。
14
+ *
15
+ * Phase 3 (multimodal-attachment-llm-dispatch): modelCaps 从 readPrimaryModelCaps() 动态注入。
16
+ * caps_mismatch 时构造结构化错误回响,不调用 daemon,直接 throw 供上层构造 run.failed。
17
+ *
18
+ * Phase 5: 迁移到 relayObjectToBlock 共享模块。
19
+ * 通过 relayObjectToBlock 处理 relay_object blocks(含 attachment_kind 字段),
20
+ * sign-get + fetch + base64 全套由 relayObjectToBlock 内嵌处理,
21
+ * 支持 agent_app Phase 5 改造后的 agentlink-cos://<cosObjectKey> URL 格式。
22
+ *
23
+ * spec ref: openspec/changes/multimodal-attachment-llm-dispatch
24
+ * specs/cli-multimodal-dispatch/spec.md
25
+ * specs/openclaw-model-caps/spec.md
26
+ *
27
+ * @param {string} textInput — dispatch payload 的 input 字段(字符串)
28
+ * @param {Array} blocks — 经过 preprocessAttachmentsToText 处理后的 blocks 数组
29
+ * @param {{
30
+ * fetchAndBase64Encode?: Function,
31
+ * modelCaps?: string[],
32
+ * _readPrimaryModelCaps?: () => Promise<string[]>,
33
+ * }} opts — 依赖注入(测试 mock 用)
34
+ * @returns {Promise<string | Array>}
35
+ * - 纯文本路径(无 relay_object blocks):返回字符串 textInput
36
+ * - 多模态路径:返回 [{type:"message", role:"user", content:[...]}]
37
+ * - caps_mismatch 时:throw { code: "caps_mismatch", message, model, missing_modality }
38
+ */
39
+ import { fetchAndBase64Encode as _defaultFetchB64 } from "../_shared/fetchAndBase64Encode.mjs";
40
+ import { relayObjectToBlock } from "../_shared/relayObjectToBlock.mjs";
41
+ import { readPrimaryModelCaps as _defaultReadCaps } from "./openclawConfig.mjs";
42
+
43
+ export async function buildOpenclawDaemonInput(textInput, blocks, {
44
+ fetchAndBase64Encode: _fetchB64,
45
+ modelCaps: _injectedCaps,
46
+ _readPrimaryModelCaps,
47
+ } = {}) {
48
+ const doFetchB64 = _fetchB64 ?? _defaultFetchB64;
49
+
50
+ // Task 3.3: modelCaps 从 readPrimaryModelCaps 动态取,支持测试注入
51
+ const readCaps = _readPrimaryModelCaps ?? _defaultReadCaps;
52
+ const modelCaps = _injectedCaps ?? await readCaps();
53
+
54
+ // relay_object blocks:type=image 或 type=attachment,source.kind=url,有 url
55
+ // 同时支持带 attachment_kind(新格式)和不带(旧格式,relayObjectToBlock 内部推断)
56
+ const relayBlocks = (blocks ?? []).filter(
57
+ (b) =>
58
+ (b?.type === "image" || b?.type === "attachment") &&
59
+ b?.source?.kind === "url" &&
60
+ b?.source?.url,
61
+ );
62
+
63
+ // 若无 relay_object blocks → 返回 textInput 字符串(向后兼容,保持纯文本路径不变)
64
+ if (relayBlocks.length === 0) {
65
+ return textInput;
66
+ }
67
+
68
+ // 构造 content array
69
+ const content = [];
70
+
71
+ // 1. textInput 非空时先加 input_text。
72
+ // openclaw daemon /v1/responses 接到只有 input_image / input_file 的
73
+ // user message 会回 400 "Missing user message in `input`."(responses
74
+ // API spec 要求 user message 至少带一段 input_text)。App 端用户发图
75
+ // 时常常 text 为空 → 这里兜底注入一个最小化的 input_text,让 daemon
76
+ // 把 image / pdf 当成 user 输入正常处理。
77
+ const effectiveText = textInput && textInput.length > 0
78
+ ? textInput
79
+ : "(用户发送了附件,请基于附件回复)";
80
+ content.push({ type: "input_text", text: effectiveText });
81
+
82
+ // 2. 遍历 relay_object blocks,通过 relayObjectToBlock 处理。
83
+ // Task 3.3: modelCaps 从 readPrimaryModelCaps 动态取(上方已解析)。
84
+ // relayObjectToBlock 内嵌处理:sign-get + fetch + base64,
85
+ // 支持 agentlink-cos://<cosObjectKey> URL 格式。
86
+ for (const block of relayBlocks) {
87
+ const result = await relayObjectToBlock(block, {
88
+ runtimeHint: "openclaw",
89
+ modelCaps,
90
+ fetchAndBase64Encode: doFetchB64,
91
+ });
92
+
93
+ if (result.error) {
94
+ // Task 4.1: caps_mismatch → throw 结构化错误,由上层构造 run.failed(不静默降级)
95
+ if (result.error.code === "caps_mismatch") {
96
+ const err = new Error(result.error.message);
97
+ err.code = "caps_mismatch";
98
+ err.capsMismatch = result.error;
99
+ throw err;
100
+ }
101
+ // fetch_failed / size_exceeded / unsupported_kind → 占位 input_text,不让整个调用挂掉
102
+ const name = block?.name ?? block?.source?.media_type ?? "file";
103
+ const label = block?.type === "image" ? "image" : `file: ${name}`;
104
+ content.push({
105
+ type: "input_text",
106
+ text: `[${label}] (encoding failed: ${result.error.message})`,
107
+ });
108
+ continue;
109
+ }
110
+
111
+ // 映射 relayObjectToBlock 结果到 openclaw daemon content block 格式
112
+ // openclaw 使用 input_image / input_file / input_text(与 relayObjectToBlock 输出一致)
113
+ switch (result.type) {
114
+ case "input_image":
115
+ content.push({
116
+ type: "input_image",
117
+ source: result.source,
118
+ });
119
+ break;
120
+ case "input_file":
121
+ content.push({
122
+ type: "input_file",
123
+ source: result.source,
124
+ });
125
+ break;
126
+ case "input_text":
127
+ // text 文件内容 / binary 描述 / document text extraction fallback
128
+ content.push({ type: "input_text", text: result.text });
129
+ break;
130
+ case "url_ref":
131
+ // picoclaw only — openclaw/claude runtimes never return url_ref from relayObjectToBlock;
132
+ // if this branch is somehow hit, drop to a safe description with NO URL in LLM context
133
+ // (铁律 #1: signed URL must not appear in LLM input). (CR-003)
134
+ content.push({ type: "input_text", text: `[image] (preview unavailable for this runtime)` });
135
+ break;
136
+ default:
137
+ break;
138
+ }
139
+ }
140
+
141
+ // 封装为 messages array
142
+ return [{ type: "message", role: "user", content }];
143
+ }
@@ -0,0 +1,91 @@
1
+ // mcpConfigAdapter.mjs — OpenClaw 的 MCP server 配置注入器(stub)。
2
+ //
3
+ // OpenClaw 2026.3.13 没有 `mcp` 子命令(实测 `openclaw mcp list` →
4
+ // "unknown command 'mcp'")。OpenClaw 通过 **plugin 系统**(`openclaw plugins
5
+ // install/list/info/...`)扩展功能,agentlink 已经在 `npm_cli/src-ext/openclaw-plugin/`
6
+ // 里注册了 `agentlink-hooks` plugin。
7
+ //
8
+ // **这里的 register/deregister 是 no-op stub**——真正的 tool injection
9
+ // 在 `openclaw-plugin/index.cjs` 里调 `api.tools.register(...)`(见 section 2d
10
+ // 实现)。本 adapter 存在的目的只是让 `agentlink mcp list/status` 子命令的
11
+ // 输出能跟 4 个 runtime 对齐 shape。
12
+ //
13
+ // status() 通过 `openclaw plugins list` 子进程探测 plugin 是否真在 enabled。
14
+
15
+ import { execFile } from "node:child_process";
16
+ import { promisify } from "node:util";
17
+ import { existsSync, statSync } from "node:fs";
18
+ import path from "node:path";
19
+ import os from "node:os";
20
+ const execFileP = promisify(execFile);
21
+
22
+ export const fileExtension = "json"; // 没文件可备份, 但 fileExtension 字段是必须的
23
+
24
+ const PLUGIN_ID = "agentlink-hooks";
25
+
26
+ /** 检查 OpenClaw 是否在本机装过(看 ~/.openclaw/ 目录,**不**执行 openclaw 二进制)。
27
+ * 执行 `openclaw --version` 会触发它的 plugin 加载 / 首次 init,$HOME 重定向
28
+ * 到测试 tmp_path 时这一步可能挂(实测) —— 改成 fs 探测,瞬时返回。 */
29
+ function isOpenclawInstalled() {
30
+ try {
31
+ const st = statSync(path.join(os.homedir(), ".openclaw"));
32
+ return st.isDirectory();
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * read() 给 fanout core 用来生成 backup,但 OpenClaw 不需要备份(plugin 安装
40
+ * 状态由 OpenClaw 自己管)。返回 null,fanout core 会跳过备份。
41
+ */
42
+ export async function read() {
43
+ return null;
44
+ }
45
+
46
+ /**
47
+ * 注册 stub。返回 mode:"plugin_managed" 告诉 caller "tool 由 plugin 接管,
48
+ * 这里不动配置文件"。**不**执行 openclaw 二进制(可能慢/挂)。
49
+ */
50
+ export async function register({ log } = {}) {
51
+ const installed = isOpenclawInstalled();
52
+ log?.info?.("mcp.fanout.openclaw.stub", "openclaw uses plugin tool injection", {
53
+ installed,
54
+ });
55
+ return {
56
+ ok: true,
57
+ changed: false,
58
+ mode: "plugin_managed",
59
+ installed,
60
+ };
61
+ }
62
+
63
+ export async function deregister({ log } = {}) {
64
+ log?.info?.("mcp.fanout.openclaw.stub", "no MCP config to remove (plugin-managed)");
65
+ return {
66
+ ok: true,
67
+ changed: false,
68
+ mode: "plugin_managed",
69
+ };
70
+ }
71
+
72
+ /**
73
+ * status() 通过 ~/.openclaw/ 目录是否存在判 OpenClaw 是否装。**不**执行
74
+ * openclaw 二进制(避免 5s timeout 或卡死;openclaw plugin 加载是重操作)。
75
+ * plugin 是否真注册需要 OpenClaw daemon 通过 `openclaw plugins list` 看,
76
+ * 这里只能报"plugin_managed"占位,真实状态由 plugin 自己往 OpenClaw 日志写。
77
+ */
78
+ export async function status() {
79
+ if (!isOpenclawInstalled()) {
80
+ return { installed: false, registered: false, reason: "openclaw_not_installed" };
81
+ }
82
+ return {
83
+ installed: true,
84
+ // plugin 管理 — adapter 看不到 plugin 是否真注册了 agentlink tools,只能假
85
+ // 设 plugin 在跑就 registered。用户可通过 `openclaw plugins list` 手动确认。
86
+ registered: true,
87
+ mode: "plugin_managed",
88
+ pluginId: PLUGIN_ID,
89
+ pluginToolInjectionStatus: "unknown_from_adapter",
90
+ };
91
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * openclawConfig.mjs — Task 3.1
3
+ *
4
+ * 读取 ~/.openclaw/openclaw.json,解析 primary model 的 compat.modalities。
5
+ *
6
+ * 公共 API:
7
+ * readPrimaryModelCaps({ _readFile? }) → Promise<string[]>
8
+ *
9
+ * 路径查找逻辑:
10
+ * 1. agents.defaults.model.primary → 格式为 "<providerName>/<modelId>"
11
+ * 2. 在 models.providers[providerName].models[] 里找 id === modelId
12
+ * 3. 返回 compat.modalities(缺失时返回 ["text"])
13
+ *
14
+ * 失败安全策略(任何异常均返回 ["text"],不 throw):
15
+ * - 文件不存在 → console.warn + 返回 ["text"]
16
+ * - JSON 解析失败 → console.warn + 返回 ["text"]
17
+ * - primary model 路径找不到 → console.warn + 返回 ["text"]
18
+ * - compat.modalities 缺失 → 返回 ["text"](最保守,silently)
19
+ *
20
+ * spec ref: openspec/changes/multimodal-attachment-llm-dispatch
21
+ * specs/openclaw-model-caps/spec.md D6
22
+ */
23
+
24
+ import { readFile as _nodeReadFile } from "node:fs/promises";
25
+ import { homedir } from "node:os";
26
+ import { join } from "node:path";
27
+
28
+ const DEFAULT_CAPS = Object.freeze(["text"]);
29
+
30
+ /**
31
+ * openclaw.json のデフォルトパスを返す。
32
+ */
33
+ function defaultConfigPath() {
34
+ return join(homedir(), ".openclaw", "openclaw.json");
35
+ }
36
+
37
+ /**
38
+ * primary model の modalities を読み取る。
39
+ *
40
+ * @param {{ _readFile?: (path: string) => Promise<string> }} [opts]
41
+ * - _readFile: テスト用依存注入(デフォルト: node:fs/promises readFile)
42
+ * @returns {Promise<string[]>}
43
+ */
44
+ export async function readPrimaryModelCaps({ _readFile } = {}) {
45
+ const readFile = _readFile ?? ((p) => _nodeReadFile(p, "utf-8"));
46
+ const configPath = defaultConfigPath();
47
+
48
+ let raw;
49
+ try {
50
+ raw = await readFile(configPath);
51
+ } catch (err) {
52
+ console.warn(
53
+ `[openclawConfig] openclaw.json not found or unreadable (${err?.code ?? err?.message ?? err}), ` +
54
+ `defaulting to caps=["text"]`
55
+ );
56
+ return [...DEFAULT_CAPS];
57
+ }
58
+
59
+ let cfg;
60
+ try {
61
+ cfg = JSON.parse(raw);
62
+ } catch (err) {
63
+ console.warn(
64
+ `[openclawConfig] openclaw.json JSON parse failed (${err?.message ?? err}), ` +
65
+ `defaulting to caps=["text"]`
66
+ );
67
+ return [...DEFAULT_CAPS];
68
+ }
69
+
70
+ // agents.defaults.model.primary → "<providerName>/<modelId>"
71
+ const primaryRef = cfg?.agents?.defaults?.model?.primary;
72
+ if (!primaryRef || typeof primaryRef !== "string") {
73
+ console.warn(
74
+ `[openclawConfig] agents.defaults.model.primary missing or not a string, ` +
75
+ `defaulting to caps=["text"]`
76
+ );
77
+ return [...DEFAULT_CAPS];
78
+ }
79
+
80
+ // "<providerName>/<modelId>" を分割
81
+ const slashIdx = primaryRef.indexOf("/");
82
+ if (slashIdx === -1) {
83
+ console.warn(
84
+ `[openclawConfig] primary model ref "${primaryRef}" has no provider prefix (expected "provider/modelId"), ` +
85
+ `defaulting to caps=["text"]`
86
+ );
87
+ return [...DEFAULT_CAPS];
88
+ }
89
+ const providerName = primaryRef.slice(0, slashIdx);
90
+ const modelId = primaryRef.slice(slashIdx + 1);
91
+
92
+ // providers[providerName].models[] を検索
93
+ const providers = cfg?.models?.providers;
94
+ if (!providers || typeof providers !== "object") {
95
+ console.warn(
96
+ `[openclawConfig] models.providers not found in openclaw.json, ` +
97
+ `defaulting to caps=["text"]`
98
+ );
99
+ return [...DEFAULT_CAPS];
100
+ }
101
+
102
+ const provider = providers[providerName];
103
+ if (!provider) {
104
+ console.warn(
105
+ `[openclawConfig] provider "${providerName}" not found in models.providers, ` +
106
+ `defaulting to caps=["text"]`
107
+ );
108
+ return [...DEFAULT_CAPS];
109
+ }
110
+
111
+ const models = provider?.models;
112
+ if (!Array.isArray(models)) {
113
+ console.warn(
114
+ `[openclawConfig] provider "${providerName}" has no models array, ` +
115
+ `defaulting to caps=["text"]`
116
+ );
117
+ return [...DEFAULT_CAPS];
118
+ }
119
+
120
+ const entry = models.find((m) => m?.id === modelId);
121
+ if (!entry) {
122
+ console.warn(
123
+ `[openclawConfig] model id "${modelId}" not found under provider "${providerName}", ` +
124
+ `defaulting to caps=["text"]`
125
+ );
126
+ return [...DEFAULT_CAPS];
127
+ }
128
+
129
+ // 兼容两种 schema:
130
+ // * 早期 / CLI 约定:`compat.modalities: ["text","image",...]`
131
+ // * openclaw daemon 官方 schema(2026.3+):`input: ["text","image",...]`
132
+ // —— openclaw config 校验器只认 `input`,`compat.modalities` 会被报
133
+ // "Unrecognized key" 直接拒掉整份 config。两边字段不统一是个已知问题,
134
+ // reader 这里同时支持两个键,有任一就用,优先 compat.modalities(向前兼容)。
135
+ // 缺失时 silently 返回 ["text"](最保守)。
136
+ const fromCompat = entry?.compat?.modalities;
137
+ const fromInput = entry?.input;
138
+ const modalities = Array.isArray(fromCompat) && fromCompat.length > 0
139
+ ? fromCompat
140
+ : (Array.isArray(fromInput) && fromInput.length > 0 ? fromInput : null);
141
+ if (!modalities) {
142
+ return [...DEFAULT_CAPS];
143
+ }
144
+ return [...modalities];
145
+ }