@halofy/agent-connect 0.12.0 → 0.13.0

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 (253) hide show
  1. package/README.md +228 -12
  2. package/bin/halofy-agent.mjs +83 -8
  3. package/node_modules/yaml/LICENSE +13 -0
  4. package/node_modules/yaml/README.md +172 -0
  5. package/node_modules/yaml/bin.mjs +11 -0
  6. package/node_modules/yaml/browser/dist/compose/compose-collection.js +88 -0
  7. package/node_modules/yaml/browser/dist/compose/compose-doc.js +43 -0
  8. package/node_modules/yaml/browser/dist/compose/compose-node.js +109 -0
  9. package/node_modules/yaml/browser/dist/compose/compose-scalar.js +86 -0
  10. package/node_modules/yaml/browser/dist/compose/composer.js +219 -0
  11. package/node_modules/yaml/browser/dist/compose/resolve-block-map.js +115 -0
  12. package/node_modules/yaml/browser/dist/compose/resolve-block-scalar.js +198 -0
  13. package/node_modules/yaml/browser/dist/compose/resolve-block-seq.js +49 -0
  14. package/node_modules/yaml/browser/dist/compose/resolve-end.js +37 -0
  15. package/node_modules/yaml/browser/dist/compose/resolve-flow-collection.js +207 -0
  16. package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +226 -0
  17. package/node_modules/yaml/browser/dist/compose/resolve-props.js +146 -0
  18. package/node_modules/yaml/browser/dist/compose/util-contains-newline.js +34 -0
  19. package/node_modules/yaml/browser/dist/compose/util-empty-scalar-position.js +26 -0
  20. package/node_modules/yaml/browser/dist/compose/util-flow-indent-check.js +15 -0
  21. package/node_modules/yaml/browser/dist/compose/util-map-includes.js +13 -0
  22. package/node_modules/yaml/browser/dist/doc/Document.js +335 -0
  23. package/node_modules/yaml/browser/dist/doc/anchors.js +71 -0
  24. package/node_modules/yaml/browser/dist/doc/applyReviver.js +55 -0
  25. package/node_modules/yaml/browser/dist/doc/createNode.js +88 -0
  26. package/node_modules/yaml/browser/dist/doc/directives.js +176 -0
  27. package/node_modules/yaml/browser/dist/errors.js +57 -0
  28. package/node_modules/yaml/browser/dist/index.js +17 -0
  29. package/node_modules/yaml/browser/dist/log.js +11 -0
  30. package/node_modules/yaml/browser/dist/nodes/Alias.js +118 -0
  31. package/node_modules/yaml/browser/dist/nodes/Collection.js +147 -0
  32. package/node_modules/yaml/browser/dist/nodes/Node.js +38 -0
  33. package/node_modules/yaml/browser/dist/nodes/Pair.js +36 -0
  34. package/node_modules/yaml/browser/dist/nodes/Scalar.js +24 -0
  35. package/node_modules/yaml/browser/dist/nodes/YAMLMap.js +144 -0
  36. package/node_modules/yaml/browser/dist/nodes/YAMLSeq.js +113 -0
  37. package/node_modules/yaml/browser/dist/nodes/addPairToJSMap.js +63 -0
  38. package/node_modules/yaml/browser/dist/nodes/identity.js +36 -0
  39. package/node_modules/yaml/browser/dist/nodes/toJS.js +37 -0
  40. package/node_modules/yaml/browser/dist/parse/cst-scalar.js +214 -0
  41. package/node_modules/yaml/browser/dist/parse/cst-stringify.js +61 -0
  42. package/node_modules/yaml/browser/dist/parse/cst-visit.js +97 -0
  43. package/node_modules/yaml/browser/dist/parse/cst.js +98 -0
  44. package/node_modules/yaml/browser/dist/parse/lexer.js +721 -0
  45. package/node_modules/yaml/browser/dist/parse/line-counter.js +39 -0
  46. package/node_modules/yaml/browser/dist/parse/parser.js +975 -0
  47. package/node_modules/yaml/browser/dist/public-api.js +102 -0
  48. package/node_modules/yaml/browser/dist/schema/Schema.js +37 -0
  49. package/node_modules/yaml/browser/dist/schema/common/map.js +17 -0
  50. package/node_modules/yaml/browser/dist/schema/common/null.js +15 -0
  51. package/node_modules/yaml/browser/dist/schema/common/seq.js +17 -0
  52. package/node_modules/yaml/browser/dist/schema/common/string.js +14 -0
  53. package/node_modules/yaml/browser/dist/schema/core/bool.js +19 -0
  54. package/node_modules/yaml/browser/dist/schema/core/float.js +43 -0
  55. package/node_modules/yaml/browser/dist/schema/core/int.js +38 -0
  56. package/node_modules/yaml/browser/dist/schema/core/schema.js +23 -0
  57. package/node_modules/yaml/browser/dist/schema/json/schema.js +62 -0
  58. package/node_modules/yaml/browser/dist/schema/tags.js +96 -0
  59. package/node_modules/yaml/browser/dist/schema/yaml-1.1/binary.js +58 -0
  60. package/node_modules/yaml/browser/dist/schema/yaml-1.1/bool.js +26 -0
  61. package/node_modules/yaml/browser/dist/schema/yaml-1.1/float.js +46 -0
  62. package/node_modules/yaml/browser/dist/schema/yaml-1.1/int.js +71 -0
  63. package/node_modules/yaml/browser/dist/schema/yaml-1.1/merge.js +67 -0
  64. package/node_modules/yaml/browser/dist/schema/yaml-1.1/omap.js +74 -0
  65. package/node_modules/yaml/browser/dist/schema/yaml-1.1/pairs.js +78 -0
  66. package/node_modules/yaml/browser/dist/schema/yaml-1.1/schema.js +39 -0
  67. package/node_modules/yaml/browser/dist/schema/yaml-1.1/set.js +93 -0
  68. package/node_modules/yaml/browser/dist/schema/yaml-1.1/timestamp.js +101 -0
  69. package/node_modules/yaml/browser/dist/stringify/foldFlowLines.js +146 -0
  70. package/node_modules/yaml/browser/dist/stringify/stringify.js +129 -0
  71. package/node_modules/yaml/browser/dist/stringify/stringifyCollection.js +153 -0
  72. package/node_modules/yaml/browser/dist/stringify/stringifyComment.js +20 -0
  73. package/node_modules/yaml/browser/dist/stringify/stringifyDocument.js +85 -0
  74. package/node_modules/yaml/browser/dist/stringify/stringifyNumber.js +25 -0
  75. package/node_modules/yaml/browser/dist/stringify/stringifyPair.js +150 -0
  76. package/node_modules/yaml/browser/dist/stringify/stringifyString.js +336 -0
  77. package/node_modules/yaml/browser/dist/util.js +11 -0
  78. package/node_modules/yaml/browser/dist/visit.js +233 -0
  79. package/node_modules/yaml/browser/index.js +5 -0
  80. package/node_modules/yaml/browser/package.json +3 -0
  81. package/node_modules/yaml/dist/cli.d.ts +8 -0
  82. package/node_modules/yaml/dist/cli.mjs +201 -0
  83. package/node_modules/yaml/dist/compose/compose-collection.d.ts +11 -0
  84. package/node_modules/yaml/dist/compose/compose-collection.js +90 -0
  85. package/node_modules/yaml/dist/compose/compose-doc.d.ts +7 -0
  86. package/node_modules/yaml/dist/compose/compose-doc.js +45 -0
  87. package/node_modules/yaml/dist/compose/compose-node.d.ts +29 -0
  88. package/node_modules/yaml/dist/compose/compose-node.js +112 -0
  89. package/node_modules/yaml/dist/compose/compose-scalar.d.ts +5 -0
  90. package/node_modules/yaml/dist/compose/compose-scalar.js +88 -0
  91. package/node_modules/yaml/dist/compose/composer.d.ts +63 -0
  92. package/node_modules/yaml/dist/compose/composer.js +224 -0
  93. package/node_modules/yaml/dist/compose/resolve-block-map.d.ts +6 -0
  94. package/node_modules/yaml/dist/compose/resolve-block-map.js +117 -0
  95. package/node_modules/yaml/dist/compose/resolve-block-scalar.d.ts +11 -0
  96. package/node_modules/yaml/dist/compose/resolve-block-scalar.js +200 -0
  97. package/node_modules/yaml/dist/compose/resolve-block-seq.d.ts +6 -0
  98. package/node_modules/yaml/dist/compose/resolve-block-seq.js +51 -0
  99. package/node_modules/yaml/dist/compose/resolve-end.d.ts +6 -0
  100. package/node_modules/yaml/dist/compose/resolve-end.js +39 -0
  101. package/node_modules/yaml/dist/compose/resolve-flow-collection.d.ts +7 -0
  102. package/node_modules/yaml/dist/compose/resolve-flow-collection.js +209 -0
  103. package/node_modules/yaml/dist/compose/resolve-flow-scalar.d.ts +10 -0
  104. package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +228 -0
  105. package/node_modules/yaml/dist/compose/resolve-props.d.ts +23 -0
  106. package/node_modules/yaml/dist/compose/resolve-props.js +148 -0
  107. package/node_modules/yaml/dist/compose/util-contains-newline.d.ts +2 -0
  108. package/node_modules/yaml/dist/compose/util-contains-newline.js +36 -0
  109. package/node_modules/yaml/dist/compose/util-empty-scalar-position.d.ts +2 -0
  110. package/node_modules/yaml/dist/compose/util-empty-scalar-position.js +28 -0
  111. package/node_modules/yaml/dist/compose/util-flow-indent-check.d.ts +3 -0
  112. package/node_modules/yaml/dist/compose/util-flow-indent-check.js +17 -0
  113. package/node_modules/yaml/dist/compose/util-map-includes.d.ts +4 -0
  114. package/node_modules/yaml/dist/compose/util-map-includes.js +15 -0
  115. package/node_modules/yaml/dist/doc/Document.d.ts +141 -0
  116. package/node_modules/yaml/dist/doc/Document.js +337 -0
  117. package/node_modules/yaml/dist/doc/anchors.d.ts +24 -0
  118. package/node_modules/yaml/dist/doc/anchors.js +76 -0
  119. package/node_modules/yaml/dist/doc/applyReviver.d.ts +9 -0
  120. package/node_modules/yaml/dist/doc/applyReviver.js +57 -0
  121. package/node_modules/yaml/dist/doc/createNode.d.ts +17 -0
  122. package/node_modules/yaml/dist/doc/createNode.js +90 -0
  123. package/node_modules/yaml/dist/doc/directives.d.ts +49 -0
  124. package/node_modules/yaml/dist/doc/directives.js +178 -0
  125. package/node_modules/yaml/dist/errors.d.ts +21 -0
  126. package/node_modules/yaml/dist/errors.js +62 -0
  127. package/node_modules/yaml/dist/index.d.ts +25 -0
  128. package/node_modules/yaml/dist/index.js +50 -0
  129. package/node_modules/yaml/dist/log.d.ts +3 -0
  130. package/node_modules/yaml/dist/log.js +19 -0
  131. package/node_modules/yaml/dist/nodes/Alias.d.ts +29 -0
  132. package/node_modules/yaml/dist/nodes/Alias.js +120 -0
  133. package/node_modules/yaml/dist/nodes/Collection.d.ts +73 -0
  134. package/node_modules/yaml/dist/nodes/Collection.js +151 -0
  135. package/node_modules/yaml/dist/nodes/Node.d.ts +53 -0
  136. package/node_modules/yaml/dist/nodes/Node.js +40 -0
  137. package/node_modules/yaml/dist/nodes/Pair.d.ts +22 -0
  138. package/node_modules/yaml/dist/nodes/Pair.js +39 -0
  139. package/node_modules/yaml/dist/nodes/Scalar.d.ts +47 -0
  140. package/node_modules/yaml/dist/nodes/Scalar.js +27 -0
  141. package/node_modules/yaml/dist/nodes/YAMLMap.d.ts +53 -0
  142. package/node_modules/yaml/dist/nodes/YAMLMap.js +147 -0
  143. package/node_modules/yaml/dist/nodes/YAMLSeq.d.ts +60 -0
  144. package/node_modules/yaml/dist/nodes/YAMLSeq.js +115 -0
  145. package/node_modules/yaml/dist/nodes/addPairToJSMap.d.ts +4 -0
  146. package/node_modules/yaml/dist/nodes/addPairToJSMap.js +65 -0
  147. package/node_modules/yaml/dist/nodes/identity.d.ts +23 -0
  148. package/node_modules/yaml/dist/nodes/identity.js +53 -0
  149. package/node_modules/yaml/dist/nodes/toJS.d.ts +29 -0
  150. package/node_modules/yaml/dist/nodes/toJS.js +39 -0
  151. package/node_modules/yaml/dist/options.d.ts +350 -0
  152. package/node_modules/yaml/dist/parse/cst-scalar.d.ts +64 -0
  153. package/node_modules/yaml/dist/parse/cst-scalar.js +218 -0
  154. package/node_modules/yaml/dist/parse/cst-stringify.d.ts +8 -0
  155. package/node_modules/yaml/dist/parse/cst-stringify.js +63 -0
  156. package/node_modules/yaml/dist/parse/cst-visit.d.ts +39 -0
  157. package/node_modules/yaml/dist/parse/cst-visit.js +99 -0
  158. package/node_modules/yaml/dist/parse/cst.d.ts +109 -0
  159. package/node_modules/yaml/dist/parse/cst.js +112 -0
  160. package/node_modules/yaml/dist/parse/lexer.d.ts +87 -0
  161. package/node_modules/yaml/dist/parse/lexer.js +723 -0
  162. package/node_modules/yaml/dist/parse/line-counter.d.ts +22 -0
  163. package/node_modules/yaml/dist/parse/line-counter.js +41 -0
  164. package/node_modules/yaml/dist/parse/parser.d.ts +84 -0
  165. package/node_modules/yaml/dist/parse/parser.js +980 -0
  166. package/node_modules/yaml/dist/public-api.d.ts +44 -0
  167. package/node_modules/yaml/dist/public-api.js +107 -0
  168. package/node_modules/yaml/dist/schema/Schema.d.ts +17 -0
  169. package/node_modules/yaml/dist/schema/Schema.js +39 -0
  170. package/node_modules/yaml/dist/schema/common/map.d.ts +2 -0
  171. package/node_modules/yaml/dist/schema/common/map.js +19 -0
  172. package/node_modules/yaml/dist/schema/common/null.d.ts +4 -0
  173. package/node_modules/yaml/dist/schema/common/null.js +17 -0
  174. package/node_modules/yaml/dist/schema/common/seq.d.ts +2 -0
  175. package/node_modules/yaml/dist/schema/common/seq.js +19 -0
  176. package/node_modules/yaml/dist/schema/common/string.d.ts +2 -0
  177. package/node_modules/yaml/dist/schema/common/string.js +16 -0
  178. package/node_modules/yaml/dist/schema/core/bool.d.ts +4 -0
  179. package/node_modules/yaml/dist/schema/core/bool.js +21 -0
  180. package/node_modules/yaml/dist/schema/core/float.d.ts +4 -0
  181. package/node_modules/yaml/dist/schema/core/float.js +47 -0
  182. package/node_modules/yaml/dist/schema/core/int.d.ts +4 -0
  183. package/node_modules/yaml/dist/schema/core/int.js +42 -0
  184. package/node_modules/yaml/dist/schema/core/schema.d.ts +1 -0
  185. package/node_modules/yaml/dist/schema/core/schema.js +25 -0
  186. package/node_modules/yaml/dist/schema/json/schema.d.ts +2 -0
  187. package/node_modules/yaml/dist/schema/json/schema.js +64 -0
  188. package/node_modules/yaml/dist/schema/json-schema.d.ts +69 -0
  189. package/node_modules/yaml/dist/schema/tags.d.ts +48 -0
  190. package/node_modules/yaml/dist/schema/tags.js +99 -0
  191. package/node_modules/yaml/dist/schema/types.d.ts +92 -0
  192. package/node_modules/yaml/dist/schema/yaml-1.1/binary.d.ts +2 -0
  193. package/node_modules/yaml/dist/schema/yaml-1.1/binary.js +70 -0
  194. package/node_modules/yaml/dist/schema/yaml-1.1/bool.d.ts +7 -0
  195. package/node_modules/yaml/dist/schema/yaml-1.1/bool.js +29 -0
  196. package/node_modules/yaml/dist/schema/yaml-1.1/float.d.ts +4 -0
  197. package/node_modules/yaml/dist/schema/yaml-1.1/float.js +50 -0
  198. package/node_modules/yaml/dist/schema/yaml-1.1/int.d.ts +5 -0
  199. package/node_modules/yaml/dist/schema/yaml-1.1/int.js +76 -0
  200. package/node_modules/yaml/dist/schema/yaml-1.1/merge.d.ts +9 -0
  201. package/node_modules/yaml/dist/schema/yaml-1.1/merge.js +71 -0
  202. package/node_modules/yaml/dist/schema/yaml-1.1/omap.d.ts +22 -0
  203. package/node_modules/yaml/dist/schema/yaml-1.1/omap.js +77 -0
  204. package/node_modules/yaml/dist/schema/yaml-1.1/pairs.d.ts +10 -0
  205. package/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +82 -0
  206. package/node_modules/yaml/dist/schema/yaml-1.1/schema.d.ts +1 -0
  207. package/node_modules/yaml/dist/schema/yaml-1.1/schema.js +41 -0
  208. package/node_modules/yaml/dist/schema/yaml-1.1/set.d.ts +28 -0
  209. package/node_modules/yaml/dist/schema/yaml-1.1/set.js +96 -0
  210. package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.d.ts +6 -0
  211. package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +105 -0
  212. package/node_modules/yaml/dist/stringify/foldFlowLines.d.ts +34 -0
  213. package/node_modules/yaml/dist/stringify/foldFlowLines.js +151 -0
  214. package/node_modules/yaml/dist/stringify/stringify.d.ts +21 -0
  215. package/node_modules/yaml/dist/stringify/stringify.js +132 -0
  216. package/node_modules/yaml/dist/stringify/stringifyCollection.d.ts +17 -0
  217. package/node_modules/yaml/dist/stringify/stringifyCollection.js +155 -0
  218. package/node_modules/yaml/dist/stringify/stringifyComment.d.ts +10 -0
  219. package/node_modules/yaml/dist/stringify/stringifyComment.js +24 -0
  220. package/node_modules/yaml/dist/stringify/stringifyDocument.d.ts +4 -0
  221. package/node_modules/yaml/dist/stringify/stringifyDocument.js +87 -0
  222. package/node_modules/yaml/dist/stringify/stringifyNumber.d.ts +2 -0
  223. package/node_modules/yaml/dist/stringify/stringifyNumber.js +27 -0
  224. package/node_modules/yaml/dist/stringify/stringifyPair.d.ts +3 -0
  225. package/node_modules/yaml/dist/stringify/stringifyPair.js +152 -0
  226. package/node_modules/yaml/dist/stringify/stringifyString.d.ts +9 -0
  227. package/node_modules/yaml/dist/stringify/stringifyString.js +338 -0
  228. package/node_modules/yaml/dist/test-events.d.ts +4 -0
  229. package/node_modules/yaml/dist/test-events.js +134 -0
  230. package/node_modules/yaml/dist/util.d.ts +16 -0
  231. package/node_modules/yaml/dist/util.js +28 -0
  232. package/node_modules/yaml/dist/visit.d.ts +102 -0
  233. package/node_modules/yaml/dist/visit.js +236 -0
  234. package/node_modules/yaml/package.json +97 -0
  235. package/node_modules/yaml/util.js +2 -0
  236. package/package.json +8 -2
  237. package/src/claude-config.mjs +60 -2
  238. package/src/claude-hook.mjs +20 -7
  239. package/src/client-registry.mjs +13 -0
  240. package/src/delivery-sync.mjs +14 -0
  241. package/src/health.mjs +15 -0
  242. package/src/hermes-config.mjs +283 -0
  243. package/src/hermes-health.mjs +45 -0
  244. package/src/hermes-hook.mjs +86 -0
  245. package/src/hermes-plugin.py +67 -0
  246. package/src/host-hook.mjs +4 -7
  247. package/src/install.mjs +10 -2
  248. package/src/installer-cli.mjs +25 -3
  249. package/src/instructions.mjs +34 -15
  250. package/src/runtime.mjs +1 -1
  251. package/src/skill-guard.mjs +171 -0
  252. package/src/skills-sync.mjs +32 -16
  253. package/src/version.mjs +3 -3
package/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # Halofy agent lifecycle installer
2
2
 
3
- Status: published installer (`@halofy/agent-connect@0.10.0`). The deployment
4
- workflow pins the verified tarball; host loading requires separate verification.
3
+ Status: source for `@halofy/agent-connect@0.13.0`; publication and deployment
4
+ require separate verification. The production console supplies the command for
5
+ the verified published package pinned by the deployment workflow. Host loading
6
+ requires separate verification.
5
7
 
6
8
  This package contains the host-neutral client pieces for installation-bound
7
9
  agent connections:
@@ -22,12 +24,80 @@ conversations and serves the agent-invoked MCP memory tools, but does not push
22
24
  recalled memory into host sessions on session start or prompt submit. The
23
25
  bounded recall block formats stay in place and tested for when it returns.
24
26
 
25
- ## Browser sign-in (0.12.0 source)
27
+ ## Headless host limits and explicit refresh
26
28
 
27
- Users assigned to a team can run the installer without a manually copied badge:
29
+ Codex `exec` lifecycle hooks are not verified. A local Codex CLI 0.154.0
30
+ probe reached session/model dispatch without invoking configured SessionStart
31
+ or UserPromptSubmit hooks. Do not infer startup delivery, capture or complete
32
+ conversation coverage from the presence of `hooks.json`. This source adds an
33
+ explicit refresh command for an existing installation:
28
34
 
29
35
  ```bash
30
- npx @halofy/agent-connect@0.12.0 install claude-code --server https://app.halofy.ai
36
+ node "/absolute/path/to/runtime/0.13.0/bin/halofy-agent.mjs" sync --connection <installation-id>
37
+ ```
38
+
39
+ The installer prints `syncCommand` with the exact executable, argument array
40
+ and `HALOFY_AGENT_HOME` environment for this installation. Use those values
41
+ (the example path above is a placeholder); an `npx` installation does not put
42
+ `halofy-agent` on your shell PATH. Run it before starting headless work. It
43
+ refreshes instructions, policies, knowledge references and assigned skills through the same signed, authorized delivery path without creating a
44
+ host session or capturing messages. It exits nonzero if instructions or
45
+ acknowledged file delivery are unavailable. A successful refresh proves file
46
+ delivery only; start a new host session to load changes. It does not add hooks
47
+ to `codex exec` or enable headless capture. Existing 0.12.1 installations do
48
+ not acquire this command until the new package is published and installed.
49
+
50
+ Claude installation adds a read-only permission for the exact installation's
51
+ `halofy-context-<hash>` directory, never the whole skills tree, runtime keys,
52
+ shell commands or MCP tools. Existing ask/deny rules remain authoritative.
53
+ Project allow rules require a trusted workspace; `claude -p` in an untrusted
54
+ folder may ignore them. Assigned skill folders and MCP tools may still need
55
+ separate host permission. No trust markers or permission-bypass flags are set.
56
+ See [Claude permissions](https://code.claude.com/docs/en/permissions).
57
+
58
+ Guarded Claude installations give managed skills immutable version aliases and
59
+ check current eligibility before native `Skill` expansion. Approved calls keep
60
+ normal host permissions; retired, replaced or unverifiable managed versions are
61
+ denied, including bodies Claude cached before its startup sync. Unrelated
62
+ employee skills outside reserved `halofy-managed-*` aliases and legacy tombstone
63
+ names remain unchanged when ownership history is intact. This requires
64
+ the installed trusted hook to execute and a reachable Halofy server. It cannot
65
+ recall earlier conversation content, downloaded copies or arbitrary file reads.
66
+ Other trusted hooks that rewrite `Skill` input are incompatible with this guard:
67
+ Claude can apply their rewritten input after Halofy's check.
68
+ Installer compatibility diagnostics inspect project, project-local and user
69
+ settings. An empty overlap list does not verify plugin, managed or command-line
70
+ hooks, or hooks introduced after installation.
71
+
72
+ If ownership history is missing or corrupt, all native Skill calls pause because
73
+ the runtime cannot safely distinguish employee skills from legacy managed names.
74
+ An administrator must restore verified `claude-skill-guard` ownership state or
75
+ review a reconnect migration that preserves legacy tombstones. Do not delete or
76
+ recreate an empty history to bypass the pause. Ordinary reinstall does not reset
77
+ initialized history.
78
+
79
+ The isolated real-host regression is opt-in (no global configuration or real
80
+ credentials): `HALOFY_CLAUDE_HOST_TEST=1 node --test test/claude-host-skills.test.mjs`.
81
+ Set `HALOFY_CLAUDE_BINARY` if `claude` is not on PATH. It verifies current managed
82
+ and employee skill expansion, native deny preservation and cached retired-body
83
+ denial, including whitespace around legacy names. It also reproduces the trusted
84
+ input-rewrite limitation in both hook registration orders.
85
+
86
+ For Codex, `HALOFY_CODEX_HOST_TEST=1 node --test test/codex-host-skills.test.mjs`
87
+ verifies signed explicit synchronization removes a retired skill's catalog and
88
+ explicitly requested body from the first fresh host request while preserving an
89
+ employee skill. Set `HALOFY_CODEX_BINARY` if needed. A running conversation can
90
+ retain already-loaded content; publication alone does not refresh installed
91
+ runtimes or their managed copies.
92
+
93
+ ## Browser sign-in
94
+
95
+ Users assigned to a team can run the installer without a manually copied badge.
96
+ Use the production console’s verified command. After 0.13.0 is published and
97
+ verified, the command for this source version is:
98
+
99
+ ```bash
100
+ npx @halofy/agent-connect@0.13.0 install claude-code --server https://app.halofy.ai
31
101
  ```
32
102
 
33
103
  `login <client-kind> --server <origin>` is an alias for the same installation
@@ -40,13 +110,12 @@ put in browser URLs. Retry a failed/expired sign-in with the same command; the
40
110
  pending key survives retries. Installation still requires the terminal's
41
111
  explicit CONNECT disclosure before changing host configuration.
42
112
 
43
- This source version requires the matching server enrollment endpoints and a
44
- published 0.12.0 package before the npx example can be used remotely. The existing
45
- manual claim path remains supported:
46
-
113
+ Browser sign-in requires the matching server enrollment endpoints. The example
114
+ above does not establish that 0.13.0 is published or enabled in production. The
115
+ manual claim path remains supported; after publication, its 0.13.0 form is:
47
116
 
48
117
  ```bash
49
- npx --yes @halofy/agent-connect@0.10.0 install <client-kind> \
118
+ npx --yes @halofy/agent-connect@0.13.0 install <client-kind> \
50
119
  --server https://app.halofy.ai \
51
120
  --claim '<one-time-claim>'
52
121
  ```
@@ -63,6 +132,7 @@ permits only these reviewed client kinds:
63
132
  | `codex` | partial | assistant responses, tool failures, session end, binary bodies |
64
133
  | `vscode` | partial | assistant responses, binary bodies, context-use evidence |
65
134
  | `cline` | partial | assistant responses, tool failures, subagents, compaction, session end, binary bodies |
135
+ | `hermes-agents` | partial; Hermes 0.21.3 (2026.9.14) only | interrupted/intermediate assistant output, compaction, tokens/thinking, subagents, binary bodies, managed instructions/skills/context |
66
136
 
67
137
  Other catalog entries remain `Not supported yet` or `Not observed`; the
68
138
  installer refuses them before claim consumption. A knowledge connector,
@@ -131,8 +201,8 @@ node kernel/integrations/agent-runtime/bin/halofy-agent.mjs diagnostics
131
201
  ```
132
202
 
133
203
  Version 0.8.0 additionally refreshes authorized organization and team policy and
134
- knowledge-base references at installation and SessionStart for all seven hosts
135
- above. Each installation gets a private `halofy-context-*` skill folder containing
204
+ knowledge-base references at installation and SessionStart for the seven pre-Hermes hosts
205
+ above. Hermes does not support this managed delivery. Each installation gets a private `halofy-context-*` skill folder containing
136
206
  `SKILL.md` and canonical content references. The content is extracted directives
137
207
  and ingested knowledge, not original uploaded files or live backend rows. Host
138
208
  skill discovery makes the references available; copying files is not evidence
@@ -220,6 +290,16 @@ memory recall. There is no resident instruction daemon; an offline device or a
220
290
  host session without an installed startup hook does not fetch updates. Claude's
221
291
  existing hook installation is project-scoped even though its rule file is global.
222
292
 
293
+ Version 0.12.1 gives instruction GET and its best-effort status receipt one shared
294
+ 8-second deadline at startup or direct refresh, and 15 seconds during interactive
295
+ installation. A failed GET preserves current instructions; a receipt timeout does
296
+ not undo a completed local update or prove server acknowledgement. Other request
297
+ and host-hook limits are unchanged; this is not a deadline guarantee for the
298
+ whole startup sequence. Startup runs instruction sync alongside skill/reference
299
+ delivery, including while capture is paused, so slow reference downloads or
300
+ receipts do not hold up the instruction fetch. Replay and heartbeat still follow
301
+ both refreshes.
302
+
223
303
  | Host | Local target | Boundaries |
224
304
  |---|---|---|
225
305
  | Codex | `CODEX_HOME` or `~/.codex`, active nonempty `AGENTS.override.md` else `AGENTS.md` | Marked section; changing the active target reports a conflict |
@@ -238,6 +318,91 @@ precedence, context limits, project configuration and exclusion settings still
238
318
  apply. Receipts say `pending_restart`, never loaded or obeyed. Other local OS
239
319
  accounts, containers and remote/cloud profiles require their own installation.
240
320
 
321
+ ### Codex instruction delivery verification
322
+
323
+ On 2026-09-17, source runtime 0.12.2 was tested against Codex CLI 0.154.0 on
324
+ Linux with disposable profiles and a loopback model endpoint. The actual host's
325
+ outgoing request contained the published instruction after signed prelaunch
326
+ sync, then its replacement, then no managed instruction after authorized
327
+ removal. Personal instruction bytes survived. This verifies that tested host's
328
+ loading path, not model obedience or delivery to an existing employee device.
329
+ The protocol server in this test is synthetic; it does not replace the kernel's
330
+ signed HTTP, scope, publication, or receipt tests.
331
+
332
+ The probe also runs the actual installer in a second fresh disposable profile:
333
+ organization disclosure and fixture confirmation, claim exchange, generated
334
+ installation proof, signed heartbeat, profile configuration, instruction write,
335
+ ownership manifest, and receipt. The first real Codex request loads that
336
+ instruction. Executing the returned `syncCommand` from the installed runtime
337
+ then replaces it in the first following fresh session. This uses synthetic
338
+ claim authority and confirmation; it is not production enrollment or employee
339
+ consent evidence.
340
+
341
+ The same probe established two independent startup limits:
342
+
343
+ - With trusted hooks, `SessionStart` refreshed the file and sent a receipt,
344
+ but Codex had already discovered the instructions for that session. The new
345
+ text appeared on the following fresh launch. `pending_restart` is accurate
346
+ even when the startup fetch succeeded.
347
+ - Without trust for the hook definition, Codex skipped the hook and made no
348
+ instruction request. Installing hook configuration does not establish that
349
+ the host executes it. Review and trust the installed hooks through Codex's
350
+ own workflow; do not disable the host's trust checks as a production remedy.
351
+
352
+ For a published instruction with no verified host delivery:
353
+
354
+ 1. Where authorized evidence is available, correlate the device, OS account,
355
+ Codex profile (`CODEX_HOME`), badge namespace, active installation, and
356
+ executable runtime version. A newer staged package or a different signed-in
357
+ console workspace proves none of
358
+ these. Never share private keys, claims, or complete connection files.
359
+ 2. If the installation predates instruction support or lacks the consented
360
+ `instructionProfile`, have its recipient complete the currently verified
361
+ setup/reconnect command for that host and badge. Do not manufacture a
362
+ profile by editing the connection JSON. A release must publish and activate
363
+ a reviewed runtime before using capabilities absent from the current pin.
364
+ 3. For a reviewed installed version that provides `syncCommand` (0.12.2 source
365
+ and later), run its exact executable, arguments, and `HALOFY_AGENT_HOME`
366
+ environment **before** starting Codex. Check successful exit and `ready:true`.
367
+ This performs signed file refresh; it neither starts capture nor upgrades
368
+ an older installed runtime. Source 0.12.2 alone is not publication evidence.
369
+ 4. Start a fresh session in the intended profile and workspace. Correlate the
370
+ effective instruction digest, owned-file manifest, server receipt, and host
371
+ load evidence. A receipt alone does not establish that the model loaded or
372
+ followed the instruction. Keep representative workflow verification separate
373
+ from claims about an affected installation that has not been inspected.
374
+
375
+ Release acceptance requires the coordinator to verify the reviewed package's
376
+ published version and integrity, the deployed installer pin, and the exact
377
+ installed version. In an authorized disposable installation, use normal scoped
378
+ publication and enrollment to repeat the first-fresh-session check, then the
379
+ replacement and removal checks through the installed `syncCommand`. Retain only
380
+ the installation/version/scope identifiers, effective bundle digest, owned-file
381
+ manifest metadata, receipt outcome, and host-load assertion. Personal content
382
+ must survive each change. A content SHA is not the effective bundle digest.
383
+ These checks can establish a supported delivery remedy without access to an
384
+ end user's device; historical incident repair remains unconfirmed until that
385
+ device's delivery is observed.
386
+
387
+ The opt-in smoke test requires a local Codex executable, uses no model API key,
388
+ and leaves user profiles untouched. It exercises the real source hook and
389
+ explicit CLI `sync`, fresh installer, and installed runtime with synthetic signed
390
+ responses. The hook-trust bypass is limited to the disposable fixture; a separate
391
+ run verifies untrusted hooks
392
+ are skipped. No raw model requests or private fixture keys are retained.
393
+
394
+ ```sh
395
+ cd kernel/integrations/agent-runtime
396
+ HALOFY_CODEX_HOST_TEST=1 node --test test/codex-host-instructions.test.mjs
397
+ ```
398
+
399
+ Set `HALOFY_CODEX_BINARY` to select another executable. The test reports its
400
+ version and observed startup timing; it is skipped in the default hermetic
401
+ suite. See the official [Codex instruction discovery](https://learn.chatgpt.com/docs/agent-configuration/agents-md),
402
+ [hook trust](https://learn.chatgpt.com/docs/hooks), and
403
+ [provider configuration](https://learn.chatgpt.com/docs/config-file/config-reference)
404
+ references for the host mechanisms used by the fixture.
405
+
241
406
  Sync validates exact content and bundle digests, scope ancestry/order and size
242
407
  before writing. Personal bytes outside a managed block are preserved exactly.
243
408
  An isolated ownership manifest, exclusive profile lock, no-follow reads,
@@ -258,3 +423,54 @@ selected profile. Local filesystem errors and receipt failures do not interrupt
258
423
  capture or MCP. Neither instruction text, filesystem paths nor backups are sent
259
424
  in status receipts. Existing policy/knowledge/skill operations and runtime queue
260
425
  files are not changed by instruction sync.
426
+
427
+
428
+ ## Hermes pinned adapter (0.13.0 source)
429
+
430
+ This source supports [Hermes Agent v2026.9.14](https://github.com/NousResearch/hermes-agent/releases/tag/v2026.9.14),
431
+ package version `0.21.3`, upstream commit
432
+ `345cd2b057a452236de401d3534b8502a7465e8d`. The installer executes
433
+ `hermes --version` and rejects other versions before consuming a claim. The
434
+ server offers Hermes only with a verified stable installer at least `0.13.0`.
435
+ Publication, server activation and each employee installation require separate
436
+ verification. Existing runtimes do not upgrade automatically.
437
+
438
+ Installation targets the selected `HERMES_HOME`, otherwise `~/.hermes`, after
439
+ explicit CONNECT consent. One owned `plugins/halofy-lifecycle` directory contains
440
+ a native Python observer (`capture`) and a portable MCP plugin (`memory`).
441
+ Both exact discovery keys are enabled. Unrelated YAML/comments/plugins remain;
442
+ ambiguous YAML, symlinks, disabled plugins, namespace collisions and edited owned
443
+ files stop installation. A known conflict is checked before claim consumption.
444
+ The portable plugin uses the native namespaced server key, not a generic
445
+ `mcp_servers.halofy` command. No credentials are stored in either plugin.
446
+
447
+ The capture plugin records submitted user text, final uninterrupted assistant
448
+ text and native tool call/results. Native end-of-turn commits a checkpoint;
449
+ only finalize closes the session. Missing, multimodal, oversized or interrupted
450
+ content produces explicit gaps. No transcript files are read. Recall remains
451
+ agent-invoked through signed MCP tools; no recalled context is injected. Managed
452
+ instructions, skills and knowledge references remain unsupported.
453
+
454
+ Before starting a new MCP proxy, the runtime validates native portable
455
+ `PLUGIN_ROOT`, independently profile-derived `PLUGIN_DATA`, the working
456
+ directory, the exact host version and owned configuration. Config-only copies
457
+ have no executable memory plugin. Profile clones, moves and symlinked plugin
458
+ subtrees must reconnect explicitly. A whole-home alias to the same canonical
459
+ directory is the same profile, not a newly enrolled profile. These checks prevent
460
+ accidental authority inheritance; they are not attestation against a process
461
+ that deliberately forges its environment and command under the same OS account
462
+ that can already read the installation proof.
463
+
464
+ The package bundles `yaml@2.9.1` under its original ISC license. Release CI installs
465
+ locked dependencies with scripts disabled; the durable runtime copy includes the
466
+ bundled dependency and license so it works after the transient package directory
467
+ is removed. `test/hermes-package.test.mjs` packs the real artifact and verifies
468
+ that offline copy. Setting `HALOFY_HERMES_PYTHON` to an isolated pinned Hermes
469
+ interpreter additionally runs native discovery and synthetic local-model turns,
470
+ checks signed lifecycle/MCP requests, and exercises rejected profile copies.
471
+ This is host/protocol verification, not real-provider inference or production
472
+ connection evidence.
473
+
474
+ Manual bearer MCP remains a separate existing memory-tools-only alternative;
475
+ see the [official Hermes MCP configuration reference](https://hermes-agent.nousresearch.com/docs/reference/mcp-config-reference).
476
+ A manual MCP connection is not lifecycle capture evidence.
@@ -1,13 +1,31 @@
1
1
  #!/usr/bin/env node
2
+ import { skillInvocationDenial } from "../src/skill-guard.mjs";
2
3
  import { ConnectionStore, defaultRuntimeDirectory } from "../src/storage.mjs";
3
4
  import { loadActiveConnection } from "../src/active.mjs";
4
5
  import { runStdioMcpProxy } from "../src/mcp-proxy.mjs";
5
6
  import { BoundedEncryptedQueue } from "../src/queue.mjs";
6
7
  import { runClaudeLifecycleHook } from "../src/claude-hook.mjs";
7
8
  import { HOST_HOOK_EVENTS, runHostLifecycleHook } from "../src/host-hook.mjs";
9
+ import { HERMES_HOOK_EVENTS, runHermesLifecycleHook } from "../src/hermes-hook.mjs";
8
10
  import { LifecycleRuntime } from "../src/runtime.mjs";
9
11
  import { setCapturePaused } from "../src/health.mjs";
10
- import { join } from "node:path";
12
+ import { refreshManagedConfiguration } from "../src/delivery-sync.mjs";
13
+ import { join, resolve } from "node:path";
14
+ import { realpath } from "node:fs/promises";
15
+ import { readJson, safeDirectory } from "../src/storage.mjs";
16
+ import { createHash } from "node:crypto";
17
+ import { readPrivateJson } from "../src/context-sync.mjs";
18
+
19
+ async function loadGuardConnection(installationId) {
20
+ const root = defaultRuntimeDirectory();
21
+ const id = installationId || process.env.HALOFY_INSTALLATION_ID
22
+ || (await readPrivateJson(join(root, "active-claude-code.json")))?.installationId;
23
+ const connection = await readPrivateJson(new ConnectionStore(root).path(id));
24
+ if (connection?.clientKind !== "claude-code" || connection.installationId !== id) {
25
+ throw new Error("Claude skill guard connection identity is unavailable");
26
+ }
27
+ return connection;
28
+ }
11
29
 
12
30
  function option(name) {
13
31
  const index = process.argv.indexOf(name);
@@ -15,31 +33,85 @@ function option(name) {
15
33
  }
16
34
 
17
35
  const command = process.argv[2];
18
- const hookEvent = command === "hook" ? process.argv[3] : null;
36
+ const hookEvent = command === "hook" || command === "hermes-hook" ? process.argv[3] : null;
37
+ // Bound connection/input loading as well as the guard itself below Claude's
38
+ // eight-second timeout. Fail closed with a flushed native decision.
39
+ const guardDeadline = command === "hook" && hookEvent === "PreToolUse" ? setTimeout(() => {
40
+ process.stdout.write(`${JSON.stringify(skillInvocationDenial())}\n`, () => process.exit(0));
41
+ }, 6000) : null;
19
42
  const claudeHookEvents = new Set([
20
- "SessionStart", "UserPromptSubmit", "Stop", "PreCompact", "SessionEnd",
43
+ "PreToolUse", "SessionStart", "UserPromptSubmit", "Stop", "PreCompact", "SessionEnd",
21
44
  "SubagentStart", "SubagentStop", "PostToolUse", "PostToolUseFailure", "PostToolBatch",
22
45
  ]);
23
- if (!["mcp", "diagnostics", "hook", "pause", "resume"].includes(command) ||
46
+ if (!["mcp", "hermes-mcp", "diagnostics", "hook", "hermes-hook", "pause", "resume", "sync"].includes(command) ||
47
+ (command === "hermes-hook" && !HERMES_HOOK_EVENTS.has(hookEvent)) ||
24
48
  (command === "hook" && !claudeHookEvents.has(hookEvent) && !HOST_HOOK_EVENTS.has(hookEvent))) {
25
- process.stderr.write("Usage: halofy-agent <mcp|diagnostics|hook EVENT|pause|resume> [--connection <installation-id>]\n");
49
+ process.stderr.write("Usage: halofy-agent <mcp|diagnostics|hook EVENT|pause|resume|sync> [--connection <installation-id>]\n");
26
50
  process.exitCode = 2;
27
51
  } else {
28
52
  try {
53
+ if (command === "hermes-mcp") {
54
+ const expected = option("--hermes-home");
55
+ const actual = process.env.PLUGIN_ROOT;
56
+ const enrolledPlugin = expected && join(resolve(expected), "plugins", "halofy-lifecycle", "memory");
57
+ const namespace = `agent-plugin-halofy-lifecycle-memory-${createHash("sha256").update("halofy-lifecycle/memory").digest("hex").slice(0, 8)}`;
58
+ const enrolledData = expected && join(resolve(expected), "plugin-data", namespace);
59
+ const actualData = process.env.PLUGIN_DATA;
60
+ // The pinned native portable loader supplies PLUGIN_ROOT and defaults cwd
61
+ // to the discovered package. HERMES_HOME is intentionally stripped from
62
+ // MCP children; a copied descriptor must not authenticate its old profile.
63
+ if (!expected || !actual || !actualData || !process.env.HALOFY_AGENT_HOME ||
64
+ resolve(actual) !== enrolledPlugin || await realpath(actual) !== enrolledPlugin ||
65
+ resolve(actualData) !== enrolledData || await realpath(actualData) !== enrolledData ||
66
+ process.cwd() !== enrolledPlugin) {
67
+ throw new Error("this Hermes profile is not enrolled; connect it explicitly");
68
+ }
69
+ await safeDirectory(enrolledPlugin);
70
+ await safeDirectory(enrolledData);
71
+ // A host upgrade can make Hermes skip the plugin while retaining its
72
+ // MCP config. Verify the reviewed host before any new proxy heartbeat.
73
+ const { detectHermes } = await import("../src/hermes-config.mjs");
74
+ detectHermes();
75
+ const id = option("--connection");
76
+ if (!id || !/^[A-Za-z0-9_-]{1,160}$/.test(id)) throw new Error("invalid Hermes installation");
77
+ const metadata = await readJson(join(defaultRuntimeDirectory(), id, "health-targets.json"));
78
+ const target = metadata?.targets?.find((entry) => entry.kind === "hermes");
79
+ const { inspectHermesCaptureHealth } = await import("../src/hermes-health.mjs");
80
+ if (!target || target.configPath !== join(resolve(expected), "config.yaml") ||
81
+ target.pluginPath !== join(resolve(expected), "plugins", "halofy-lifecycle") ||
82
+ (await inspectHermesCaptureHealth(target)).captureState !== "active") {
83
+ throw new Error("managed Hermes profile configuration changed; reconnect explicitly");
84
+ }
85
+ }
29
86
  const installationId = option("--connection");
30
- const connection = installationId
87
+ const connection = hookEvent === "PreToolUse" ? await loadGuardConnection(installationId) : installationId
31
88
  ? await new ConnectionStore(defaultRuntimeDirectory()).load(installationId)
32
89
  : await loadActiveConnection("claude-code");
33
90
  if (!connection) throw new Error("no active installation-bound connection");
34
- if (command === "mcp") {
91
+ if (command === "mcp" || command === "hermes-mcp") {
92
+ if (command === "hermes-mcp" && connection.clientKind !== "hermes-agents") {
93
+ throw new Error("Hermes profile requires a Hermes installation");
94
+ }
35
95
  await runStdioMcpProxy(connection);
96
+ } else if (command === "sync") {
97
+ const root = defaultRuntimeDirectory();
98
+ const result = await refreshManagedConfiguration(new LifecycleRuntime(connection, { root }), connection, root);
99
+ process.stdout.write(`${JSON.stringify(result)}\n`);
100
+ if (!result.ready) process.exitCode = 1;
36
101
  } else if (command === "pause" || command === "resume") {
37
102
  const root = defaultRuntimeDirectory();
38
103
  const runtime = new LifecycleRuntime(connection, { root });
39
104
  process.stdout.write(`${JSON.stringify(await setCapturePaused(runtime, root, command === "pause"))}\n`);
105
+ } else if (command === "hermes-hook") {
106
+ const result = await runHermesLifecycleHook(connection, hookEvent);
107
+ if (result.degraded || result.unavailable) process.exitCode = 1;
40
108
  } else if (command === "hook") {
41
109
  if (connection.clientKind === "claude-code") await runClaudeLifecycleHook(connection, hookEvent);
42
110
  else await runHostLifecycleHook(connection, hookEvent);
111
+ if (hookEvent === "PreToolUse") {
112
+ await new Promise(resolve => process.stdout.write("", resolve));
113
+ process.exit(0);
114
+ }
43
115
  } else {
44
116
  const queue = new BoundedEncryptedQueue(join(defaultRuntimeDirectory(), connection.installationId));
45
117
  process.stdout.write(`${JSON.stringify({
@@ -52,7 +124,10 @@ if (!["mcp", "diagnostics", "hook", "pause", "resume"].includes(command) ||
52
124
  }, null, 2)}\n`);
53
125
  }
54
126
  } catch (error) {
127
+ if (hookEvent === "PreToolUse") process.stdout.write(`${JSON.stringify(skillInvocationDenial())}\n`);
55
128
  process.stderr.write(`${error?.message || "Halofy MCP proxy unavailable"}\n`);
56
- process.exitCode = 1;
129
+ process.exitCode = hookEvent === "PreToolUse" ? 0 : 1;
57
130
  }
58
131
  }
132
+
133
+ if (guardDeadline) clearTimeout(guardDeadline);
@@ -0,0 +1,13 @@
1
+ Copyright Eemeli Aro <eemeli@gmail.com>
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any purpose
4
+ with or without fee is hereby granted, provided that the above copyright notice
5
+ and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
9
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
11
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
12
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
13
+ THIS SOFTWARE.
@@ -0,0 +1,172 @@
1
+ # YAML <a href="https://www.npmjs.com/package/yaml"><img align="right" src="https://badge.fury.io/js/yaml.svg" title="npm package" /></a>
2
+
3
+ `yaml` is a definitive library for [YAML](https://yaml.org/), the human friendly data serialization standard.
4
+ This library:
5
+
6
+ - Supports both YAML 1.1 and YAML 1.2 and all common data schemas,
7
+ - Passes all of the [yaml-test-suite](https://github.com/yaml/yaml-test-suite) tests,
8
+ - Can accept any string as input without throwing, parsing as much YAML out of it as it can, and
9
+ - Supports parsing, modifying, and writing YAML comments and blank lines.
10
+
11
+ The library is released under the ISC open source license, and the code is [available on GitHub](https://github.com/eemeli/yaml/).
12
+ It has no external dependencies and runs on Node.js as well as modern browsers.
13
+
14
+ For the purposes of versioning, any changes that break any of the documented endpoints or APIs will be considered semver-major breaking changes.
15
+ Undocumented library internals may change between minor versions, and previous APIs may be deprecated (but not removed).
16
+
17
+ The minimum supported TypeScript version of the included typings is 3.9;
18
+ for use in earlier versions you may need to set `skipLibCheck: true` in your config.
19
+ This requirement may be updated between minor versions of the library.
20
+
21
+ For more information, see the project's documentation site: [**eemeli.org/yaml**](https://eemeli.org/yaml/)
22
+
23
+ For build instructions and contribution guidelines, see [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md).
24
+
25
+ To install:
26
+
27
+ ```sh
28
+ npm install yaml
29
+ # or
30
+ deno add jsr:@eemeli/yaml
31
+ ```
32
+
33
+ **Note:** These docs are for `yaml@2`. For v1, see the [v1.10.0 tag](https://github.com/eemeli/yaml/tree/v1.10.0) for the source and [eemeli.org/yaml/v1](https://eemeli.org/yaml/v1/) for the documentation.
34
+
35
+ ## API Overview
36
+
37
+ The API provided by `yaml` has three layers, depending on how deep you need to go: [Parse & Stringify](https://eemeli.org/yaml/#parse-amp-stringify), [Documents](https://eemeli.org/yaml/#documents), and the underlying [Lexer/Parser/Composer](https://eemeli.org/yaml/#parsing-yaml).
38
+ The first has the simplest API and "just works", the second gets you all the bells and whistles supported by the library along with a decent [AST](https://eemeli.org/yaml/#content-nodes), and the third lets you get progressively closer to YAML source, if that's your thing.
39
+
40
+ A [command-line tool](https://eemeli.org/yaml/#command-line-tool) is also included.
41
+
42
+ ### Parse & Stringify
43
+
44
+ ```js
45
+ import { parse, stringify } from 'yaml'
46
+ ```
47
+
48
+ - [`parse(str, reviver?, options?): value`](https://eemeli.org/yaml/#yaml-parse)
49
+ - [`stringify(value, replacer?, options?): string`](https://eemeli.org/yaml/#yaml-stringify)
50
+
51
+ ### Documents
52
+
53
+ <!-- prettier-ignore -->
54
+ ```js
55
+ import {
56
+ Document,
57
+ isDocument,
58
+ parseAllDocuments,
59
+ parseDocument
60
+ } from 'yaml'
61
+ ```
62
+
63
+ - [`Document`](https://eemeli.org/yaml/#documents)
64
+ - [`constructor(value, replacer?, options?)`](https://eemeli.org/yaml/#creating-documents)
65
+ - [`#contents`](https://eemeli.org/yaml/#content-nodes)
66
+ - [`#directives`](https://eemeli.org/yaml/#stream-directives)
67
+ - [`#errors`](https://eemeli.org/yaml/#errors)
68
+ - [`#warnings`](https://eemeli.org/yaml/#errors)
69
+ - [`isDocument(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
70
+ - [`parseAllDocuments(str, options?): Document[]`](https://eemeli.org/yaml/#parsing-documents)
71
+ - [`parseDocument(str, options?): Document`](https://eemeli.org/yaml/#parsing-documents)
72
+
73
+ ### Content Nodes
74
+
75
+ <!-- prettier-ignore -->
76
+ ```js
77
+ import {
78
+ isAlias, isCollection, isMap, isNode,
79
+ isPair, isScalar, isSeq, Scalar,
80
+ visit, visitAsync, YAMLMap, YAMLSeq
81
+ } from 'yaml'
82
+ ```
83
+
84
+ - [`isAlias(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
85
+ - [`isCollection(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
86
+ - [`isMap(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
87
+ - [`isNode(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
88
+ - [`isPair(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
89
+ - [`isScalar(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
90
+ - [`isSeq(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
91
+ - [`new Scalar(value)`](https://eemeli.org/yaml/#scalar-values)
92
+ - [`new YAMLMap()`](https://eemeli.org/yaml/#collections)
93
+ - [`new YAMLSeq()`](https://eemeli.org/yaml/#collections)
94
+ - [`doc.createAlias(node, name?): Alias`](https://eemeli.org/yaml/#creating-nodes)
95
+ - [`doc.createNode(value, options?): Node`](https://eemeli.org/yaml/#creating-nodes)
96
+ - [`doc.createPair(key, value): Pair`](https://eemeli.org/yaml/#creating-nodes)
97
+ - [`visit(node, visitor)`](https://eemeli.org/yaml/#finding-and-modifying-nodes)
98
+ - [`visitAsync(node, visitor)`](https://eemeli.org/yaml/#finding-and-modifying-nodes)
99
+
100
+ ### Parsing YAML
101
+
102
+ ```js
103
+ import { Composer, Lexer, Parser } from 'yaml'
104
+ ```
105
+
106
+ - [`new Lexer().lex(src)`](https://eemeli.org/yaml/#lexer)
107
+ - [`new Parser(onNewLine?).parse(src)`](https://eemeli.org/yaml/#parser)
108
+ - [`new Composer(options?).compose(tokens)`](https://eemeli.org/yaml/#composer)
109
+
110
+ ## YAML.parse
111
+
112
+ ```yaml
113
+ # file.yml
114
+ YAML:
115
+ - A human-readable data serialization language
116
+ - https://en.wikipedia.org/wiki/YAML
117
+ yaml:
118
+ - A complete JavaScript implementation
119
+ - https://www.npmjs.com/package/yaml
120
+ ```
121
+
122
+ ```js
123
+ import fs from 'fs'
124
+ import YAML from 'yaml'
125
+
126
+ YAML.parse('3.14159')
127
+ // 3.14159
128
+
129
+ YAML.parse('[ true, false, maybe, null ]\n')
130
+ // [ true, false, 'maybe', null ]
131
+
132
+ const file = fs.readFileSync('./file.yml', 'utf8')
133
+ YAML.parse(file)
134
+ // { YAML:
135
+ // [ 'A human-readable data serialization language',
136
+ // 'https://en.wikipedia.org/wiki/YAML' ],
137
+ // yaml:
138
+ // [ 'A complete JavaScript implementation',
139
+ // 'https://www.npmjs.com/package/yaml' ] }
140
+ ```
141
+
142
+ ## YAML.stringify
143
+
144
+ ```js
145
+ import YAML from 'yaml'
146
+
147
+ YAML.stringify(3.14159)
148
+ // '3.14159\n'
149
+
150
+ YAML.stringify([true, false, 'maybe', null])
151
+ // `- true
152
+ // - false
153
+ // - maybe
154
+ // - null
155
+ // `
156
+
157
+ YAML.stringify({ number: 3, plain: 'string', block: 'two\nlines\n' })
158
+ // `number: 3
159
+ // plain: string
160
+ // block: |
161
+ // two
162
+ // lines
163
+ // `
164
+ ```
165
+
166
+ ---
167
+
168
+ Browser testing provided by:
169
+
170
+ <a href="https://www.browserstack.com/open-source">
171
+ <img width=200 src="https://eemeli.org/yaml/images/browserstack.svg" alt="BrowserStack" />
172
+ </a>
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { UserError, cli, help } from './dist/cli.mjs'
4
+
5
+ cli(process.stdin, error => {
6
+ if (error instanceof UserError) {
7
+ if (error.code === UserError.ARGS) console.error(`${help}\n`)
8
+ console.error(error.message)
9
+ process.exitCode = error.code
10
+ } else if (error) throw error
11
+ })