@halofy/agent-connect 0.12.1 → 0.13.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.
- package/README.md +249 -10
- package/bin/halofy-agent.mjs +96 -11
- package/node_modules/yaml/LICENSE +13 -0
- package/node_modules/yaml/README.md +172 -0
- package/node_modules/yaml/bin.mjs +11 -0
- package/node_modules/yaml/browser/dist/compose/compose-collection.js +88 -0
- package/node_modules/yaml/browser/dist/compose/compose-doc.js +43 -0
- package/node_modules/yaml/browser/dist/compose/compose-node.js +109 -0
- package/node_modules/yaml/browser/dist/compose/compose-scalar.js +86 -0
- package/node_modules/yaml/browser/dist/compose/composer.js +219 -0
- package/node_modules/yaml/browser/dist/compose/resolve-block-map.js +115 -0
- package/node_modules/yaml/browser/dist/compose/resolve-block-scalar.js +198 -0
- package/node_modules/yaml/browser/dist/compose/resolve-block-seq.js +49 -0
- package/node_modules/yaml/browser/dist/compose/resolve-end.js +37 -0
- package/node_modules/yaml/browser/dist/compose/resolve-flow-collection.js +207 -0
- package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +226 -0
- package/node_modules/yaml/browser/dist/compose/resolve-props.js +146 -0
- package/node_modules/yaml/browser/dist/compose/util-contains-newline.js +34 -0
- package/node_modules/yaml/browser/dist/compose/util-empty-scalar-position.js +26 -0
- package/node_modules/yaml/browser/dist/compose/util-flow-indent-check.js +15 -0
- package/node_modules/yaml/browser/dist/compose/util-map-includes.js +13 -0
- package/node_modules/yaml/browser/dist/doc/Document.js +335 -0
- package/node_modules/yaml/browser/dist/doc/anchors.js +71 -0
- package/node_modules/yaml/browser/dist/doc/applyReviver.js +55 -0
- package/node_modules/yaml/browser/dist/doc/createNode.js +88 -0
- package/node_modules/yaml/browser/dist/doc/directives.js +176 -0
- package/node_modules/yaml/browser/dist/errors.js +57 -0
- package/node_modules/yaml/browser/dist/index.js +17 -0
- package/node_modules/yaml/browser/dist/log.js +11 -0
- package/node_modules/yaml/browser/dist/nodes/Alias.js +118 -0
- package/node_modules/yaml/browser/dist/nodes/Collection.js +147 -0
- package/node_modules/yaml/browser/dist/nodes/Node.js +38 -0
- package/node_modules/yaml/browser/dist/nodes/Pair.js +36 -0
- package/node_modules/yaml/browser/dist/nodes/Scalar.js +24 -0
- package/node_modules/yaml/browser/dist/nodes/YAMLMap.js +144 -0
- package/node_modules/yaml/browser/dist/nodes/YAMLSeq.js +113 -0
- package/node_modules/yaml/browser/dist/nodes/addPairToJSMap.js +63 -0
- package/node_modules/yaml/browser/dist/nodes/identity.js +36 -0
- package/node_modules/yaml/browser/dist/nodes/toJS.js +37 -0
- package/node_modules/yaml/browser/dist/parse/cst-scalar.js +214 -0
- package/node_modules/yaml/browser/dist/parse/cst-stringify.js +61 -0
- package/node_modules/yaml/browser/dist/parse/cst-visit.js +97 -0
- package/node_modules/yaml/browser/dist/parse/cst.js +98 -0
- package/node_modules/yaml/browser/dist/parse/lexer.js +721 -0
- package/node_modules/yaml/browser/dist/parse/line-counter.js +39 -0
- package/node_modules/yaml/browser/dist/parse/parser.js +975 -0
- package/node_modules/yaml/browser/dist/public-api.js +102 -0
- package/node_modules/yaml/browser/dist/schema/Schema.js +37 -0
- package/node_modules/yaml/browser/dist/schema/common/map.js +17 -0
- package/node_modules/yaml/browser/dist/schema/common/null.js +15 -0
- package/node_modules/yaml/browser/dist/schema/common/seq.js +17 -0
- package/node_modules/yaml/browser/dist/schema/common/string.js +14 -0
- package/node_modules/yaml/browser/dist/schema/core/bool.js +19 -0
- package/node_modules/yaml/browser/dist/schema/core/float.js +43 -0
- package/node_modules/yaml/browser/dist/schema/core/int.js +38 -0
- package/node_modules/yaml/browser/dist/schema/core/schema.js +23 -0
- package/node_modules/yaml/browser/dist/schema/json/schema.js +62 -0
- package/node_modules/yaml/browser/dist/schema/tags.js +96 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/binary.js +58 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/bool.js +26 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/float.js +46 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/int.js +71 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/merge.js +67 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/omap.js +74 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/pairs.js +78 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/schema.js +39 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/set.js +93 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/timestamp.js +101 -0
- package/node_modules/yaml/browser/dist/stringify/foldFlowLines.js +146 -0
- package/node_modules/yaml/browser/dist/stringify/stringify.js +129 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyCollection.js +153 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyComment.js +20 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyDocument.js +85 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyNumber.js +25 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyPair.js +150 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyString.js +336 -0
- package/node_modules/yaml/browser/dist/util.js +11 -0
- package/node_modules/yaml/browser/dist/visit.js +233 -0
- package/node_modules/yaml/browser/index.js +5 -0
- package/node_modules/yaml/browser/package.json +3 -0
- package/node_modules/yaml/dist/cli.d.ts +8 -0
- package/node_modules/yaml/dist/cli.mjs +201 -0
- package/node_modules/yaml/dist/compose/compose-collection.d.ts +11 -0
- package/node_modules/yaml/dist/compose/compose-collection.js +90 -0
- package/node_modules/yaml/dist/compose/compose-doc.d.ts +7 -0
- package/node_modules/yaml/dist/compose/compose-doc.js +45 -0
- package/node_modules/yaml/dist/compose/compose-node.d.ts +29 -0
- package/node_modules/yaml/dist/compose/compose-node.js +112 -0
- package/node_modules/yaml/dist/compose/compose-scalar.d.ts +5 -0
- package/node_modules/yaml/dist/compose/compose-scalar.js +88 -0
- package/node_modules/yaml/dist/compose/composer.d.ts +63 -0
- package/node_modules/yaml/dist/compose/composer.js +224 -0
- package/node_modules/yaml/dist/compose/resolve-block-map.d.ts +6 -0
- package/node_modules/yaml/dist/compose/resolve-block-map.js +117 -0
- package/node_modules/yaml/dist/compose/resolve-block-scalar.d.ts +11 -0
- package/node_modules/yaml/dist/compose/resolve-block-scalar.js +200 -0
- package/node_modules/yaml/dist/compose/resolve-block-seq.d.ts +6 -0
- package/node_modules/yaml/dist/compose/resolve-block-seq.js +51 -0
- package/node_modules/yaml/dist/compose/resolve-end.d.ts +6 -0
- package/node_modules/yaml/dist/compose/resolve-end.js +39 -0
- package/node_modules/yaml/dist/compose/resolve-flow-collection.d.ts +7 -0
- package/node_modules/yaml/dist/compose/resolve-flow-collection.js +209 -0
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.d.ts +10 -0
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +228 -0
- package/node_modules/yaml/dist/compose/resolve-props.d.ts +23 -0
- package/node_modules/yaml/dist/compose/resolve-props.js +148 -0
- package/node_modules/yaml/dist/compose/util-contains-newline.d.ts +2 -0
- package/node_modules/yaml/dist/compose/util-contains-newline.js +36 -0
- package/node_modules/yaml/dist/compose/util-empty-scalar-position.d.ts +2 -0
- package/node_modules/yaml/dist/compose/util-empty-scalar-position.js +28 -0
- package/node_modules/yaml/dist/compose/util-flow-indent-check.d.ts +3 -0
- package/node_modules/yaml/dist/compose/util-flow-indent-check.js +17 -0
- package/node_modules/yaml/dist/compose/util-map-includes.d.ts +4 -0
- package/node_modules/yaml/dist/compose/util-map-includes.js +15 -0
- package/node_modules/yaml/dist/doc/Document.d.ts +141 -0
- package/node_modules/yaml/dist/doc/Document.js +337 -0
- package/node_modules/yaml/dist/doc/anchors.d.ts +24 -0
- package/node_modules/yaml/dist/doc/anchors.js +76 -0
- package/node_modules/yaml/dist/doc/applyReviver.d.ts +9 -0
- package/node_modules/yaml/dist/doc/applyReviver.js +57 -0
- package/node_modules/yaml/dist/doc/createNode.d.ts +17 -0
- package/node_modules/yaml/dist/doc/createNode.js +90 -0
- package/node_modules/yaml/dist/doc/directives.d.ts +49 -0
- package/node_modules/yaml/dist/doc/directives.js +178 -0
- package/node_modules/yaml/dist/errors.d.ts +21 -0
- package/node_modules/yaml/dist/errors.js +62 -0
- package/node_modules/yaml/dist/index.d.ts +25 -0
- package/node_modules/yaml/dist/index.js +50 -0
- package/node_modules/yaml/dist/log.d.ts +3 -0
- package/node_modules/yaml/dist/log.js +19 -0
- package/node_modules/yaml/dist/nodes/Alias.d.ts +29 -0
- package/node_modules/yaml/dist/nodes/Alias.js +120 -0
- package/node_modules/yaml/dist/nodes/Collection.d.ts +73 -0
- package/node_modules/yaml/dist/nodes/Collection.js +151 -0
- package/node_modules/yaml/dist/nodes/Node.d.ts +53 -0
- package/node_modules/yaml/dist/nodes/Node.js +40 -0
- package/node_modules/yaml/dist/nodes/Pair.d.ts +22 -0
- package/node_modules/yaml/dist/nodes/Pair.js +39 -0
- package/node_modules/yaml/dist/nodes/Scalar.d.ts +47 -0
- package/node_modules/yaml/dist/nodes/Scalar.js +27 -0
- package/node_modules/yaml/dist/nodes/YAMLMap.d.ts +53 -0
- package/node_modules/yaml/dist/nodes/YAMLMap.js +147 -0
- package/node_modules/yaml/dist/nodes/YAMLSeq.d.ts +60 -0
- package/node_modules/yaml/dist/nodes/YAMLSeq.js +115 -0
- package/node_modules/yaml/dist/nodes/addPairToJSMap.d.ts +4 -0
- package/node_modules/yaml/dist/nodes/addPairToJSMap.js +65 -0
- package/node_modules/yaml/dist/nodes/identity.d.ts +23 -0
- package/node_modules/yaml/dist/nodes/identity.js +53 -0
- package/node_modules/yaml/dist/nodes/toJS.d.ts +29 -0
- package/node_modules/yaml/dist/nodes/toJS.js +39 -0
- package/node_modules/yaml/dist/options.d.ts +350 -0
- package/node_modules/yaml/dist/parse/cst-scalar.d.ts +64 -0
- package/node_modules/yaml/dist/parse/cst-scalar.js +218 -0
- package/node_modules/yaml/dist/parse/cst-stringify.d.ts +8 -0
- package/node_modules/yaml/dist/parse/cst-stringify.js +63 -0
- package/node_modules/yaml/dist/parse/cst-visit.d.ts +39 -0
- package/node_modules/yaml/dist/parse/cst-visit.js +99 -0
- package/node_modules/yaml/dist/parse/cst.d.ts +109 -0
- package/node_modules/yaml/dist/parse/cst.js +112 -0
- package/node_modules/yaml/dist/parse/lexer.d.ts +87 -0
- package/node_modules/yaml/dist/parse/lexer.js +723 -0
- package/node_modules/yaml/dist/parse/line-counter.d.ts +22 -0
- package/node_modules/yaml/dist/parse/line-counter.js +41 -0
- package/node_modules/yaml/dist/parse/parser.d.ts +84 -0
- package/node_modules/yaml/dist/parse/parser.js +980 -0
- package/node_modules/yaml/dist/public-api.d.ts +44 -0
- package/node_modules/yaml/dist/public-api.js +107 -0
- package/node_modules/yaml/dist/schema/Schema.d.ts +17 -0
- package/node_modules/yaml/dist/schema/Schema.js +39 -0
- package/node_modules/yaml/dist/schema/common/map.d.ts +2 -0
- package/node_modules/yaml/dist/schema/common/map.js +19 -0
- package/node_modules/yaml/dist/schema/common/null.d.ts +4 -0
- package/node_modules/yaml/dist/schema/common/null.js +17 -0
- package/node_modules/yaml/dist/schema/common/seq.d.ts +2 -0
- package/node_modules/yaml/dist/schema/common/seq.js +19 -0
- package/node_modules/yaml/dist/schema/common/string.d.ts +2 -0
- package/node_modules/yaml/dist/schema/common/string.js +16 -0
- package/node_modules/yaml/dist/schema/core/bool.d.ts +4 -0
- package/node_modules/yaml/dist/schema/core/bool.js +21 -0
- package/node_modules/yaml/dist/schema/core/float.d.ts +4 -0
- package/node_modules/yaml/dist/schema/core/float.js +47 -0
- package/node_modules/yaml/dist/schema/core/int.d.ts +4 -0
- package/node_modules/yaml/dist/schema/core/int.js +42 -0
- package/node_modules/yaml/dist/schema/core/schema.d.ts +1 -0
- package/node_modules/yaml/dist/schema/core/schema.js +25 -0
- package/node_modules/yaml/dist/schema/json/schema.d.ts +2 -0
- package/node_modules/yaml/dist/schema/json/schema.js +64 -0
- package/node_modules/yaml/dist/schema/json-schema.d.ts +69 -0
- package/node_modules/yaml/dist/schema/tags.d.ts +48 -0
- package/node_modules/yaml/dist/schema/tags.js +99 -0
- package/node_modules/yaml/dist/schema/types.d.ts +92 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/binary.d.ts +2 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/binary.js +70 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/bool.d.ts +7 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/bool.js +29 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/float.d.ts +4 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/float.js +50 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/int.d.ts +5 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/int.js +76 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/merge.d.ts +9 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/merge.js +71 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/omap.d.ts +22 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/omap.js +77 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/pairs.d.ts +10 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +82 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/schema.d.ts +1 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/schema.js +41 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/set.d.ts +28 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/set.js +96 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.d.ts +6 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +105 -0
- package/node_modules/yaml/dist/stringify/foldFlowLines.d.ts +34 -0
- package/node_modules/yaml/dist/stringify/foldFlowLines.js +151 -0
- package/node_modules/yaml/dist/stringify/stringify.d.ts +21 -0
- package/node_modules/yaml/dist/stringify/stringify.js +132 -0
- package/node_modules/yaml/dist/stringify/stringifyCollection.d.ts +17 -0
- package/node_modules/yaml/dist/stringify/stringifyCollection.js +155 -0
- package/node_modules/yaml/dist/stringify/stringifyComment.d.ts +10 -0
- package/node_modules/yaml/dist/stringify/stringifyComment.js +24 -0
- package/node_modules/yaml/dist/stringify/stringifyDocument.d.ts +4 -0
- package/node_modules/yaml/dist/stringify/stringifyDocument.js +87 -0
- package/node_modules/yaml/dist/stringify/stringifyNumber.d.ts +2 -0
- package/node_modules/yaml/dist/stringify/stringifyNumber.js +27 -0
- package/node_modules/yaml/dist/stringify/stringifyPair.d.ts +3 -0
- package/node_modules/yaml/dist/stringify/stringifyPair.js +152 -0
- package/node_modules/yaml/dist/stringify/stringifyString.d.ts +9 -0
- package/node_modules/yaml/dist/stringify/stringifyString.js +338 -0
- package/node_modules/yaml/dist/test-events.d.ts +4 -0
- package/node_modules/yaml/dist/test-events.js +134 -0
- package/node_modules/yaml/dist/util.d.ts +16 -0
- package/node_modules/yaml/dist/util.js +28 -0
- package/node_modules/yaml/dist/visit.d.ts +102 -0
- package/node_modules/yaml/dist/visit.js +236 -0
- package/node_modules/yaml/package.json +97 -0
- package/node_modules/yaml/util.js +2 -0
- package/package.json +8 -2
- package/src/claude-config.mjs +60 -2
- package/src/claude-hook.mjs +8 -1
- package/src/client-registry.mjs +13 -0
- package/src/delivery-sync.mjs +14 -0
- package/src/health.mjs +26 -4
- package/src/hermes-config.mjs +283 -0
- package/src/hermes-health.mjs +45 -0
- package/src/hermes-hook.mjs +86 -0
- package/src/hermes-plugin.py +67 -0
- package/src/host-hook.mjs +6 -8
- package/src/install.mjs +8 -0
- package/src/installer-cli.mjs +25 -3
- package/src/instructions.mjs +5 -1
- package/src/runtime.mjs +33 -22
- package/src/skill-guard.mjs +171 -0
- package/src/skills-sync.mjs +32 -16
- package/src/version.mjs +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Halofy agent lifecycle installer
|
|
2
2
|
|
|
3
|
-
Status: source for `@halofy/agent-connect@0.
|
|
3
|
+
Status: source for `@halofy/agent-connect@0.13.1`; publication and deployment
|
|
4
4
|
require separate verification. The production console supplies the command for
|
|
5
5
|
the verified published package pinned by the deployment workflow. Host loading
|
|
6
6
|
requires separate verification.
|
|
@@ -24,14 +24,80 @@ conversations and serves the agent-invoked MCP memory tools, but does not push
|
|
|
24
24
|
recalled memory into host sessions on session start or prompt submit. The
|
|
25
25
|
bounded recall block formats stay in place and tested for when it returns.
|
|
26
26
|
|
|
27
|
+
## Headless host limits and explicit refresh
|
|
28
|
+
|
|
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:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
node "/absolute/path/to/runtime/0.13.1/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
|
+
|
|
27
93
|
## Browser sign-in
|
|
28
94
|
|
|
29
95
|
Users assigned to a team can run the installer without a manually copied badge.
|
|
30
|
-
Use the production console’s verified command. After 0.
|
|
96
|
+
Use the production console’s verified command. After 0.13.1 is published and
|
|
31
97
|
verified, the command for this source version is:
|
|
32
98
|
|
|
33
99
|
```bash
|
|
34
|
-
npx @halofy/agent-connect@0.
|
|
100
|
+
npx @halofy/agent-connect@0.13.1 install claude-code --server https://app.halofy.ai
|
|
35
101
|
```
|
|
36
102
|
|
|
37
103
|
`login <client-kind> --server <origin>` is an alias for the same installation
|
|
@@ -45,11 +111,11 @@ pending key survives retries. Installation still requires the terminal's
|
|
|
45
111
|
explicit CONNECT disclosure before changing host configuration.
|
|
46
112
|
|
|
47
113
|
Browser sign-in requires the matching server enrollment endpoints. The example
|
|
48
|
-
above does not establish that 0.
|
|
49
|
-
manual claim path remains supported; after publication, its 0.
|
|
114
|
+
above does not establish that 0.13.1 is published or enabled in production. The
|
|
115
|
+
manual claim path remains supported; after publication, its 0.13.1 form is:
|
|
50
116
|
|
|
51
117
|
```bash
|
|
52
|
-
npx --yes @halofy/agent-connect@0.
|
|
118
|
+
npx --yes @halofy/agent-connect@0.13.1 install <client-kind> \
|
|
53
119
|
--server https://app.halofy.ai \
|
|
54
120
|
--claim '<one-time-claim>'
|
|
55
121
|
```
|
|
@@ -66,6 +132,7 @@ permits only these reviewed client kinds:
|
|
|
66
132
|
| `codex` | partial | assistant responses, tool failures, session end, binary bodies |
|
|
67
133
|
| `vscode` | partial | assistant responses, binary bodies, context-use evidence |
|
|
68
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 |
|
|
69
136
|
|
|
70
137
|
Other catalog entries remain `Not supported yet` or `Not observed`; the
|
|
71
138
|
installer refuses them before claim consumption. A knowledge connector,
|
|
@@ -126,16 +193,52 @@ npm test
|
|
|
126
193
|
npm run check
|
|
127
194
|
```
|
|
128
195
|
|
|
129
|
-
Content-free local queue/version evidence is available without
|
|
130
|
-
|
|
196
|
+
Content-free local queue/version evidence is available without printing any
|
|
197
|
+
pending event body or proof key:
|
|
131
198
|
|
|
132
199
|
```bash
|
|
133
200
|
node kernel/integrations/agent-runtime/bin/halofy-agent.mjs diagnostics
|
|
134
201
|
```
|
|
135
202
|
|
|
203
|
+
Pass `--connection <installation-id>` to inspect a specific installation.
|
|
204
|
+
Diagnostics shows installed and running runtime versions, local hook/pause
|
|
205
|
+
health, frozen capture capabilities, queue depth, oldest pending time and expired
|
|
206
|
+
batch count. It makes no server request, and neither active local hooks nor an
|
|
207
|
+
empty queue proves that a particular conversation reached the archive.
|
|
208
|
+
|
|
209
|
+
## Conversation return reliability (0.13.1 source)
|
|
210
|
+
|
|
211
|
+
Append acknowledgement is now mandatory and bounded to the submitted batch.
|
|
212
|
+
An empty, non-JSON or malformed success response cannot discard queued events.
|
|
213
|
+
Only explicitly accepted or duplicate events advance the cursor; a server
|
|
214
|
+
sequence ahead of an event conflict cannot acknowledge the rejected local body.
|
|
215
|
+
An unchanged transcript retries its already durable queue, including after a
|
|
216
|
+
lost append response, without requiring another user message. Hook messages
|
|
217
|
+
without a native event id receive an id per invocation, so multiple prompts in
|
|
218
|
+
one Cline task and repeated identical prompts remain separate events. Queue
|
|
219
|
+
retries preserve that id; native event ids still deduplicate host retries.
|
|
220
|
+
|
|
221
|
+
Retries run at supported hooks, explicit runtime replay and the existing MCP
|
|
222
|
+
proxy's 60-second health tick. Each background tick attempts at most one pending
|
|
223
|
+
batch, with the proxy's five-second request timeout and cancellation on exit.
|
|
224
|
+
Heartbeat reporting continues when replay fails. Paused or replaced installations
|
|
225
|
+
skip background replay; resume permits queued work to retry. The pause result
|
|
226
|
+
reports an incomplete flush while any unacknowledged batch remains. No new monitoring
|
|
227
|
+
process is installed. Abrupt host shutdown can leave pending data until the next
|
|
228
|
+
proxy start or supported hook, subject to the existing seven-day queue limit.
|
|
229
|
+
This release does not widen host capabilities or add assistant
|
|
230
|
+
responses to the partial adapters listed above. Hosts without native event ids
|
|
231
|
+
cannot distinguish two host invocations from a duplicated host callback.
|
|
232
|
+
|
|
233
|
+
The npm `0.12.1` tarball was downloaded and matched the deployment workflow's
|
|
234
|
+
SHA-256 pin on 2026-09-17. These `0.13.1` fixes require a new reviewed publication,
|
|
235
|
+
server pin promotion and a recipient-confirmed reinstall. Existing runtimes do
|
|
236
|
+
not auto-upgrade. Public health responses and package integrity do not verify
|
|
237
|
+
authenticated production conversation capture.
|
|
238
|
+
|
|
136
239
|
Version 0.8.0 additionally refreshes authorized organization and team policy and
|
|
137
|
-
knowledge-base references at installation and SessionStart for
|
|
138
|
-
above. Each installation gets a private `halofy-context-*` skill folder containing
|
|
240
|
+
knowledge-base references at installation and SessionStart for the seven pre-Hermes hosts
|
|
241
|
+
above. Hermes does not support this managed delivery. Each installation gets a private `halofy-context-*` skill folder containing
|
|
139
242
|
`SKILL.md` and canonical content references. The content is extracted directives
|
|
140
243
|
and ingested knowledge, not original uploaded files or live backend rows. Host
|
|
141
244
|
skill discovery makes the references available; copying files is not evidence
|
|
@@ -251,6 +354,91 @@ precedence, context limits, project configuration and exclusion settings still
|
|
|
251
354
|
apply. Receipts say `pending_restart`, never loaded or obeyed. Other local OS
|
|
252
355
|
accounts, containers and remote/cloud profiles require their own installation.
|
|
253
356
|
|
|
357
|
+
### Codex instruction delivery verification
|
|
358
|
+
|
|
359
|
+
On 2026-09-17, source runtime 0.12.2 was tested against Codex CLI 0.154.0 on
|
|
360
|
+
Linux with disposable profiles and a loopback model endpoint. The actual host's
|
|
361
|
+
outgoing request contained the published instruction after signed prelaunch
|
|
362
|
+
sync, then its replacement, then no managed instruction after authorized
|
|
363
|
+
removal. Personal instruction bytes survived. This verifies that tested host's
|
|
364
|
+
loading path, not model obedience or delivery to an existing employee device.
|
|
365
|
+
The protocol server in this test is synthetic; it does not replace the kernel's
|
|
366
|
+
signed HTTP, scope, publication, or receipt tests.
|
|
367
|
+
|
|
368
|
+
The probe also runs the actual installer in a second fresh disposable profile:
|
|
369
|
+
organization disclosure and fixture confirmation, claim exchange, generated
|
|
370
|
+
installation proof, signed heartbeat, profile configuration, instruction write,
|
|
371
|
+
ownership manifest, and receipt. The first real Codex request loads that
|
|
372
|
+
instruction. Executing the returned `syncCommand` from the installed runtime
|
|
373
|
+
then replaces it in the first following fresh session. This uses synthetic
|
|
374
|
+
claim authority and confirmation; it is not production enrollment or employee
|
|
375
|
+
consent evidence.
|
|
376
|
+
|
|
377
|
+
The same probe established two independent startup limits:
|
|
378
|
+
|
|
379
|
+
- With trusted hooks, `SessionStart` refreshed the file and sent a receipt,
|
|
380
|
+
but Codex had already discovered the instructions for that session. The new
|
|
381
|
+
text appeared on the following fresh launch. `pending_restart` is accurate
|
|
382
|
+
even when the startup fetch succeeded.
|
|
383
|
+
- Without trust for the hook definition, Codex skipped the hook and made no
|
|
384
|
+
instruction request. Installing hook configuration does not establish that
|
|
385
|
+
the host executes it. Review and trust the installed hooks through Codex's
|
|
386
|
+
own workflow; do not disable the host's trust checks as a production remedy.
|
|
387
|
+
|
|
388
|
+
For a published instruction with no verified host delivery:
|
|
389
|
+
|
|
390
|
+
1. Where authorized evidence is available, correlate the device, OS account,
|
|
391
|
+
Codex profile (`CODEX_HOME`), badge namespace, active installation, and
|
|
392
|
+
executable runtime version. A newer staged package or a different signed-in
|
|
393
|
+
console workspace proves none of
|
|
394
|
+
these. Never share private keys, claims, or complete connection files.
|
|
395
|
+
2. If the installation predates instruction support or lacks the consented
|
|
396
|
+
`instructionProfile`, have its recipient complete the currently verified
|
|
397
|
+
setup/reconnect command for that host and badge. Do not manufacture a
|
|
398
|
+
profile by editing the connection JSON. A release must publish and activate
|
|
399
|
+
a reviewed runtime before using capabilities absent from the current pin.
|
|
400
|
+
3. For a reviewed installed version that provides `syncCommand` (0.12.2 source
|
|
401
|
+
and later), run its exact executable, arguments, and `HALOFY_AGENT_HOME`
|
|
402
|
+
environment **before** starting Codex. Check successful exit and `ready:true`.
|
|
403
|
+
This performs signed file refresh; it neither starts capture nor upgrades
|
|
404
|
+
an older installed runtime. Source 0.12.2 alone is not publication evidence.
|
|
405
|
+
4. Start a fresh session in the intended profile and workspace. Correlate the
|
|
406
|
+
effective instruction digest, owned-file manifest, server receipt, and host
|
|
407
|
+
load evidence. A receipt alone does not establish that the model loaded or
|
|
408
|
+
followed the instruction. Keep representative workflow verification separate
|
|
409
|
+
from claims about an affected installation that has not been inspected.
|
|
410
|
+
|
|
411
|
+
Release acceptance requires the coordinator to verify the reviewed package's
|
|
412
|
+
published version and integrity, the deployed installer pin, and the exact
|
|
413
|
+
installed version. In an authorized disposable installation, use normal scoped
|
|
414
|
+
publication and enrollment to repeat the first-fresh-session check, then the
|
|
415
|
+
replacement and removal checks through the installed `syncCommand`. Retain only
|
|
416
|
+
the installation/version/scope identifiers, effective bundle digest, owned-file
|
|
417
|
+
manifest metadata, receipt outcome, and host-load assertion. Personal content
|
|
418
|
+
must survive each change. A content SHA is not the effective bundle digest.
|
|
419
|
+
These checks can establish a supported delivery remedy without access to an
|
|
420
|
+
end user's device; historical incident repair remains unconfirmed until that
|
|
421
|
+
device's delivery is observed.
|
|
422
|
+
|
|
423
|
+
The opt-in smoke test requires a local Codex executable, uses no model API key,
|
|
424
|
+
and leaves user profiles untouched. It exercises the real source hook and
|
|
425
|
+
explicit CLI `sync`, fresh installer, and installed runtime with synthetic signed
|
|
426
|
+
responses. The hook-trust bypass is limited to the disposable fixture; a separate
|
|
427
|
+
run verifies untrusted hooks
|
|
428
|
+
are skipped. No raw model requests or private fixture keys are retained.
|
|
429
|
+
|
|
430
|
+
```sh
|
|
431
|
+
cd kernel/integrations/agent-runtime
|
|
432
|
+
HALOFY_CODEX_HOST_TEST=1 node --test test/codex-host-instructions.test.mjs
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
Set `HALOFY_CODEX_BINARY` to select another executable. The test reports its
|
|
436
|
+
version and observed startup timing; it is skipped in the default hermetic
|
|
437
|
+
suite. See the official [Codex instruction discovery](https://learn.chatgpt.com/docs/agent-configuration/agents-md),
|
|
438
|
+
[hook trust](https://learn.chatgpt.com/docs/hooks), and
|
|
439
|
+
[provider configuration](https://learn.chatgpt.com/docs/config-file/config-reference)
|
|
440
|
+
references for the host mechanisms used by the fixture.
|
|
441
|
+
|
|
254
442
|
Sync validates exact content and bundle digests, scope ancestry/order and size
|
|
255
443
|
before writing. Personal bytes outside a managed block are preserved exactly.
|
|
256
444
|
An isolated ownership manifest, exclusive profile lock, no-follow reads,
|
|
@@ -271,3 +459,54 @@ selected profile. Local filesystem errors and receipt failures do not interrupt
|
|
|
271
459
|
capture or MCP. Neither instruction text, filesystem paths nor backups are sent
|
|
272
460
|
in status receipts. Existing policy/knowledge/skill operations and runtime queue
|
|
273
461
|
files are not changed by instruction sync.
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
## Hermes pinned adapter (since 0.13.0)
|
|
465
|
+
|
|
466
|
+
This source supports [Hermes Agent v2026.9.14](https://github.com/NousResearch/hermes-agent/releases/tag/v2026.9.14),
|
|
467
|
+
package version `0.21.3`, upstream commit
|
|
468
|
+
`345cd2b057a452236de401d3534b8502a7465e8d`. The installer executes
|
|
469
|
+
`hermes --version` and rejects other versions before consuming a claim. The
|
|
470
|
+
server offers Hermes only with a verified stable installer at least `0.13.0`.
|
|
471
|
+
Publication, server activation and each employee installation require separate
|
|
472
|
+
verification. Existing runtimes do not upgrade automatically.
|
|
473
|
+
|
|
474
|
+
Installation targets the selected `HERMES_HOME`, otherwise `~/.hermes`, after
|
|
475
|
+
explicit CONNECT consent. One owned `plugins/halofy-lifecycle` directory contains
|
|
476
|
+
a native Python observer (`capture`) and a portable MCP plugin (`memory`).
|
|
477
|
+
Both exact discovery keys are enabled. Unrelated YAML/comments/plugins remain;
|
|
478
|
+
ambiguous YAML, symlinks, disabled plugins, namespace collisions and edited owned
|
|
479
|
+
files stop installation. A known conflict is checked before claim consumption.
|
|
480
|
+
The portable plugin uses the native namespaced server key, not a generic
|
|
481
|
+
`mcp_servers.halofy` command. No credentials are stored in either plugin.
|
|
482
|
+
|
|
483
|
+
The capture plugin records submitted user text, final uninterrupted assistant
|
|
484
|
+
text and native tool call/results. Native end-of-turn commits a checkpoint;
|
|
485
|
+
only finalize closes the session. Missing, multimodal, oversized or interrupted
|
|
486
|
+
content produces explicit gaps. No transcript files are read. Recall remains
|
|
487
|
+
agent-invoked through signed MCP tools; no recalled context is injected. Managed
|
|
488
|
+
instructions, skills and knowledge references remain unsupported.
|
|
489
|
+
|
|
490
|
+
Before starting a new MCP proxy, the runtime validates native portable
|
|
491
|
+
`PLUGIN_ROOT`, independently profile-derived `PLUGIN_DATA`, the working
|
|
492
|
+
directory, the exact host version and owned configuration. Config-only copies
|
|
493
|
+
have no executable memory plugin. Profile clones, moves and symlinked plugin
|
|
494
|
+
subtrees must reconnect explicitly. A whole-home alias to the same canonical
|
|
495
|
+
directory is the same profile, not a newly enrolled profile. These checks prevent
|
|
496
|
+
accidental authority inheritance; they are not attestation against a process
|
|
497
|
+
that deliberately forges its environment and command under the same OS account
|
|
498
|
+
that can already read the installation proof.
|
|
499
|
+
|
|
500
|
+
The package bundles `yaml@2.9.1` under its original ISC license. Release CI installs
|
|
501
|
+
locked dependencies with scripts disabled; the durable runtime copy includes the
|
|
502
|
+
bundled dependency and license so it works after the transient package directory
|
|
503
|
+
is removed. `test/hermes-package.test.mjs` packs the real artifact and verifies
|
|
504
|
+
that offline copy. Setting `HALOFY_HERMES_PYTHON` to an isolated pinned Hermes
|
|
505
|
+
interpreter additionally runs native discovery and synthetic local-model turns,
|
|
506
|
+
checks signed lifecycle/MCP requests, and exercises rejected profile copies.
|
|
507
|
+
This is host/protocol verification, not real-provider inference or production
|
|
508
|
+
connection evidence.
|
|
509
|
+
|
|
510
|
+
Manual bearer MCP remains a separate existing memory-tools-only alternative;
|
|
511
|
+
see the [official Hermes MCP configuration reference](https://hermes-agent.nousresearch.com/docs/reference/mcp-config-reference).
|
|
512
|
+
A manual MCP connection is not lifecycle capture evidence.
|
package/bin/halofy-agent.mjs
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
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
|
-
import { setCapturePaused } from "../src/health.mjs";
|
|
10
|
-
import {
|
|
11
|
+
import { inspectCaptureHealth, setCapturePaused } from "../src/health.mjs";
|
|
12
|
+
import { refreshManagedConfiguration } from "../src/delivery-sync.mjs";
|
|
13
|
+
import { RUNTIME_VERSION } from "../src/version.mjs";
|
|
14
|
+
import { join, resolve } from "node:path";
|
|
15
|
+
import { realpath } from "node:fs/promises";
|
|
16
|
+
import { readJson, safeDirectory } from "../src/storage.mjs";
|
|
17
|
+
import { createHash } from "node:crypto";
|
|
18
|
+
import { readPrivateJson } from "../src/context-sync.mjs";
|
|
19
|
+
|
|
20
|
+
async function loadGuardConnection(installationId) {
|
|
21
|
+
const root = defaultRuntimeDirectory();
|
|
22
|
+
const id = installationId || process.env.HALOFY_INSTALLATION_ID
|
|
23
|
+
|| (await readPrivateJson(join(root, "active-claude-code.json")))?.installationId;
|
|
24
|
+
const connection = await readPrivateJson(new ConnectionStore(root).path(id));
|
|
25
|
+
if (connection?.clientKind !== "claude-code" || connection.installationId !== id) {
|
|
26
|
+
throw new Error("Claude skill guard connection identity is unavailable");
|
|
27
|
+
}
|
|
28
|
+
return connection;
|
|
29
|
+
}
|
|
11
30
|
|
|
12
31
|
function option(name) {
|
|
13
32
|
const index = process.argv.indexOf(name);
|
|
@@ -15,44 +34,110 @@ function option(name) {
|
|
|
15
34
|
}
|
|
16
35
|
|
|
17
36
|
const command = process.argv[2];
|
|
18
|
-
const hookEvent = command === "hook" ? process.argv[3] : null;
|
|
37
|
+
const hookEvent = command === "hook" || command === "hermes-hook" ? process.argv[3] : null;
|
|
38
|
+
// Bound connection/input loading as well as the guard itself below Claude's
|
|
39
|
+
// eight-second timeout. Fail closed with a flushed native decision.
|
|
40
|
+
const guardDeadline = command === "hook" && hookEvent === "PreToolUse" ? setTimeout(() => {
|
|
41
|
+
process.stdout.write(`${JSON.stringify(skillInvocationDenial())}\n`, () => process.exit(0));
|
|
42
|
+
}, 6000) : null;
|
|
19
43
|
const claudeHookEvents = new Set([
|
|
20
|
-
"SessionStart", "UserPromptSubmit", "Stop", "PreCompact", "SessionEnd",
|
|
44
|
+
"PreToolUse", "SessionStart", "UserPromptSubmit", "Stop", "PreCompact", "SessionEnd",
|
|
21
45
|
"SubagentStart", "SubagentStop", "PostToolUse", "PostToolUseFailure", "PostToolBatch",
|
|
22
46
|
]);
|
|
23
|
-
if (!["mcp", "diagnostics", "hook", "pause", "resume"].includes(command) ||
|
|
47
|
+
if (!["mcp", "hermes-mcp", "diagnostics", "hook", "hermes-hook", "pause", "resume", "sync"].includes(command) ||
|
|
48
|
+
(command === "hermes-hook" && !HERMES_HOOK_EVENTS.has(hookEvent)) ||
|
|
24
49
|
(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");
|
|
50
|
+
process.stderr.write("Usage: halofy-agent <mcp|diagnostics|hook EVENT|pause|resume|sync> [--connection <installation-id>]\n");
|
|
26
51
|
process.exitCode = 2;
|
|
27
52
|
} else {
|
|
28
53
|
try {
|
|
54
|
+
if (command === "hermes-mcp") {
|
|
55
|
+
const expected = option("--hermes-home");
|
|
56
|
+
const actual = process.env.PLUGIN_ROOT;
|
|
57
|
+
const enrolledPlugin = expected && join(resolve(expected), "plugins", "halofy-lifecycle", "memory");
|
|
58
|
+
const namespace = `agent-plugin-halofy-lifecycle-memory-${createHash("sha256").update("halofy-lifecycle/memory").digest("hex").slice(0, 8)}`;
|
|
59
|
+
const enrolledData = expected && join(resolve(expected), "plugin-data", namespace);
|
|
60
|
+
const actualData = process.env.PLUGIN_DATA;
|
|
61
|
+
// The pinned native portable loader supplies PLUGIN_ROOT and defaults cwd
|
|
62
|
+
// to the discovered package. HERMES_HOME is intentionally stripped from
|
|
63
|
+
// MCP children; a copied descriptor must not authenticate its old profile.
|
|
64
|
+
if (!expected || !actual || !actualData || !process.env.HALOFY_AGENT_HOME ||
|
|
65
|
+
resolve(actual) !== enrolledPlugin || await realpath(actual) !== enrolledPlugin ||
|
|
66
|
+
resolve(actualData) !== enrolledData || await realpath(actualData) !== enrolledData ||
|
|
67
|
+
process.cwd() !== enrolledPlugin) {
|
|
68
|
+
throw new Error("this Hermes profile is not enrolled; connect it explicitly");
|
|
69
|
+
}
|
|
70
|
+
await safeDirectory(enrolledPlugin);
|
|
71
|
+
await safeDirectory(enrolledData);
|
|
72
|
+
// A host upgrade can make Hermes skip the plugin while retaining its
|
|
73
|
+
// MCP config. Verify the reviewed host before any new proxy heartbeat.
|
|
74
|
+
const { detectHermes } = await import("../src/hermes-config.mjs");
|
|
75
|
+
detectHermes();
|
|
76
|
+
const id = option("--connection");
|
|
77
|
+
if (!id || !/^[A-Za-z0-9_-]{1,160}$/.test(id)) throw new Error("invalid Hermes installation");
|
|
78
|
+
const metadata = await readJson(join(defaultRuntimeDirectory(), id, "health-targets.json"));
|
|
79
|
+
const target = metadata?.targets?.find((entry) => entry.kind === "hermes");
|
|
80
|
+
const { inspectHermesCaptureHealth } = await import("../src/hermes-health.mjs");
|
|
81
|
+
if (!target || target.configPath !== join(resolve(expected), "config.yaml") ||
|
|
82
|
+
target.pluginPath !== join(resolve(expected), "plugins", "halofy-lifecycle") ||
|
|
83
|
+
(await inspectHermesCaptureHealth(target)).captureState !== "active") {
|
|
84
|
+
throw new Error("managed Hermes profile configuration changed; reconnect explicitly");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
29
87
|
const installationId = option("--connection");
|
|
30
|
-
const connection = installationId
|
|
88
|
+
const connection = hookEvent === "PreToolUse" ? await loadGuardConnection(installationId) : installationId
|
|
31
89
|
? await new ConnectionStore(defaultRuntimeDirectory()).load(installationId)
|
|
32
90
|
: await loadActiveConnection("claude-code");
|
|
33
91
|
if (!connection) throw new Error("no active installation-bound connection");
|
|
34
|
-
if (command === "mcp") {
|
|
92
|
+
if (command === "mcp" || command === "hermes-mcp") {
|
|
93
|
+
if (command === "hermes-mcp" && connection.clientKind !== "hermes-agents") {
|
|
94
|
+
throw new Error("Hermes profile requires a Hermes installation");
|
|
95
|
+
}
|
|
35
96
|
await runStdioMcpProxy(connection);
|
|
97
|
+
} else if (command === "sync") {
|
|
98
|
+
const root = defaultRuntimeDirectory();
|
|
99
|
+
const result = await refreshManagedConfiguration(new LifecycleRuntime(connection, { root }), connection, root);
|
|
100
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
101
|
+
if (!result.ready) process.exitCode = 1;
|
|
36
102
|
} else if (command === "pause" || command === "resume") {
|
|
37
103
|
const root = defaultRuntimeDirectory();
|
|
38
104
|
const runtime = new LifecycleRuntime(connection, { root });
|
|
39
105
|
process.stdout.write(`${JSON.stringify(await setCapturePaused(runtime, root, command === "pause"))}\n`);
|
|
106
|
+
} else if (command === "hermes-hook") {
|
|
107
|
+
const result = await runHermesLifecycleHook(connection, hookEvent);
|
|
108
|
+
if (result.degraded || result.unavailable) process.exitCode = 1;
|
|
40
109
|
} else if (command === "hook") {
|
|
41
110
|
if (connection.clientKind === "claude-code") await runClaudeLifecycleHook(connection, hookEvent);
|
|
42
111
|
else await runHostLifecycleHook(connection, hookEvent);
|
|
112
|
+
if (hookEvent === "PreToolUse") {
|
|
113
|
+
await new Promise(resolve => process.stdout.write("", resolve));
|
|
114
|
+
process.exit(0);
|
|
115
|
+
}
|
|
43
116
|
} else {
|
|
44
|
-
const
|
|
117
|
+
const root = defaultRuntimeDirectory();
|
|
118
|
+
const queue = new BoundedEncryptedQueue(join(root, connection.installationId));
|
|
45
119
|
process.stdout.write(`${JSON.stringify({
|
|
46
120
|
installationId: connection.installationId,
|
|
47
121
|
clientKind: connection.clientKind,
|
|
48
122
|
protocolVersion: connection.protocolVersion,
|
|
49
123
|
pluginVersion: connection.pluginVersion,
|
|
124
|
+
runtimeVersion: RUNTIME_VERSION,
|
|
50
125
|
proofStorage: connection.proofStorage,
|
|
126
|
+
health: await inspectCaptureHealth(connection, root),
|
|
127
|
+
declaredCapabilities: Object.fromEntries([
|
|
128
|
+
"userMessages", "assistantMessages", "toolInputs", "toolOutputs", "toolFailures",
|
|
129
|
+
"sessionEnd", "tokenUsage", "sessionMetadata", "images", "artifactBodies",
|
|
130
|
+
].map((name) => [name, connection.capabilities?.[name] === true])),
|
|
131
|
+
serverCheck: "not_performed",
|
|
51
132
|
queue: await queue.diagnostics(),
|
|
52
133
|
}, null, 2)}\n`);
|
|
53
134
|
}
|
|
54
135
|
} catch (error) {
|
|
55
|
-
process.
|
|
56
|
-
process.
|
|
136
|
+
if (hookEvent === "PreToolUse") process.stdout.write(`${JSON.stringify(skillInvocationDenial())}\n`);
|
|
137
|
+
process.stderr.write(`${command === "diagnostics" ? "Halofy diagnostics unavailable" :
|
|
138
|
+
error?.message || "Halofy MCP proxy unavailable"}\n`);
|
|
139
|
+
process.exitCode = hookEvent === "PreToolUse" ? 0 : 1;
|
|
57
140
|
}
|
|
58
141
|
}
|
|
142
|
+
|
|
143
|
+
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>
|