hilos-agent 0.9.1 → 0.9.2
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 +34 -14
- package/bin/hilos-agent.mjs +3 -1
- package/package.json +1 -1
- package/src/argv.mjs +61 -0
- package/src/hook.mjs +429 -61
- package/src/mcp-loopback.mjs +142 -0
- package/src/mcp.mjs +2 -2
- package/src/progress-emitter.mjs +5 -2
- package/src/reply-bridge.mjs +412 -49
package/README.md
CHANGED
|
@@ -257,21 +257,32 @@ npx hilos-agent hooks install --cursor
|
|
|
257
257
|
npx hilos-agent hooks install --global
|
|
258
258
|
```
|
|
259
259
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
260
|
+
Claude Code and Cursor write lifecycle entries into this repo. Codex needs one
|
|
261
|
+
entry in `~/.codex/hooks.json` because `codex exec` does not run repository
|
|
262
|
+
hooks. hilos keeps that entry project-scoped with a 0600 allowlist at
|
|
263
|
+
`~/.hilos/codex-hook-scope.json`, so other repos stay untouched unless you use
|
|
264
|
+
`--global`. Launching Codex below the opted-in repository root still works, but
|
|
265
|
+
a nested repository needs its own opt-in. If the allowlist is lost or malformed,
|
|
266
|
+
new installs fail closed instead of reporting from every repo.
|
|
267
|
+
|
|
268
|
+
Any hooks you already have are preserved. A pre-existing project-level hilos
|
|
269
|
+
Codex entry is moved out, with a `.bak` beside the changed file. Codex asks you
|
|
270
|
+
to review the home-level hilos hook once; open `/hooks` and trust the entry.
|
|
271
|
+
The installer copies the dependency-free hook code into a private, versioned
|
|
272
|
+
`~/.hilos/hook-runtime` and records its absolute entrypoint. A one-off npx run
|
|
273
|
+
therefore remains usable offline; no global install or PATH change is required.
|
|
274
|
+
Preview all three blocks without writing anything:
|
|
265
275
|
|
|
266
276
|
```sh
|
|
267
277
|
npx hilos-agent hooks print
|
|
268
278
|
```
|
|
269
279
|
|
|
270
280
|
**Requirements:**
|
|
271
|
-
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
281
|
+
- For live progress from every tool event, a `~/.hilos/agent.json` with `url`
|
|
282
|
+
and the owned agent's `token`; `channelId` is needed only for the fallback
|
|
283
|
+
live card. The normal one-line `--join` flow does not need that file for
|
|
284
|
+
replies: its token stays in daemon memory, and Hilos signs each session
|
|
285
|
+
binding so the daemon can verify it before resuming local code.
|
|
275
286
|
- Keep `hilos-agent` running to pick replies up. Hooks alone still stream live
|
|
276
287
|
steps, but MCP has no server-push channel that can wake an idle local client.
|
|
277
288
|
|
|
@@ -281,7 +292,14 @@ npx hilos-agent hooks print
|
|
|
281
292
|
- A successful hilos post binds its returned message to this provider session.
|
|
282
293
|
A person's later reply to that exact thread resumes the same session while
|
|
283
294
|
`hilos-agent` is running. Ambient messages, older thread history, and agent
|
|
284
|
-
replies do not wake it.
|
|
295
|
+
replies do not wake it. Current workspace roles are checked at pickup: guests,
|
|
296
|
+
removed people, and unknown authors remain advisory and cannot start code.
|
|
297
|
+
- The resumed turn receives bounded thread, room, and member context. Codex also
|
|
298
|
+
gets a random loopback-only Hilos MCP URL for that turn, so it can search any
|
|
299
|
+
room its linked owner can access when that owner minted the current key,
|
|
300
|
+
without receiving the bearer token. Regenerate an older key from Connect to
|
|
301
|
+
enable that inheritance. Admin-issued keys and other people's private rooms
|
|
302
|
+
stay outside the context.
|
|
285
303
|
- The room's normal execution gate still applies; a chat-only guest room cannot
|
|
286
304
|
resume local code.
|
|
287
305
|
- Steps are coalesced into ~2s batches to keep traffic light.
|
|
@@ -290,10 +308,12 @@ npx hilos-agent hooks print
|
|
|
290
308
|
- Up to 20 unique files touched are surfaced so reviewers can glance at the scope
|
|
291
309
|
before the report card arrives.
|
|
292
310
|
|
|
293
|
-
**Privacy:** project-level by default (only repos you opt into stream
|
|
294
|
-
|
|
295
|
-
hilos
|
|
296
|
-
|
|
311
|
+
**Privacy:** project-level by default (only repos you opt into stream, including
|
|
312
|
+
Codex through its local allowlist). Hook state stays in
|
|
313
|
+
`~/.hilos/hook-state` and contains session ids, checkout paths,
|
|
314
|
+
hilos message ids, an opaque server-signed binding proof, a one-way credential
|
|
315
|
+
scope after verification, progress steps, and any completed result waiting for
|
|
316
|
+
a delivery retry — never the bearer token or coding transcript. Kill
|
|
297
317
|
switches: `HILOS_HOOKS=off` pauses streaming and new bindings;
|
|
298
318
|
`HILOS_REPLY_BRIDGE=off` pauses inbound replies. The hook always exits 0 — it
|
|
299
319
|
will never interrupt or break your CLI session.
|
package/bin/hilos-agent.mjs
CHANGED
|
@@ -50,6 +50,8 @@ function parseArgs(argv) {
|
|
|
50
50
|
else if (a === "--codex") flags.hookClient = "codex";
|
|
51
51
|
else if (a === "--cursor") flags.hookClient = "cursor";
|
|
52
52
|
else if (a === "--vendor") flags.vendor = argv[++i];
|
|
53
|
+
else if (a === "--scope-managed") flags.scopeManaged = true;
|
|
54
|
+
else if (a === "--managed-runtime") flags.managedRuntime = true;
|
|
53
55
|
else if (a === "-h" || a === "--help") flags.help = true;
|
|
54
56
|
else if (a === "-v" || a === "--version") flags.version = true;
|
|
55
57
|
else positional.push(a);
|
|
@@ -106,7 +108,7 @@ async function main() {
|
|
|
106
108
|
// fast, silent, and always exit 0, so it short-circuits before any daemon
|
|
107
109
|
// machinery.
|
|
108
110
|
if (cmd === "hook") {
|
|
109
|
-
await hookMain({ vendor: flags.vendor || "unknown" });
|
|
111
|
+
await hookMain({ vendor: flags.vendor || "unknown", scopeManaged: Boolean(flags.scopeManaged) });
|
|
110
112
|
return;
|
|
111
113
|
}
|
|
112
114
|
if (cmd === "hooks") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hilos-agent",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "Run your own coding agent (Claude Code / Codex / Cursor) as an autonomous teammate in a hilos channel. Picks up @mentions in channels and threads, makes the change, and opens a PR for review — your code and credentials never leave your machine. (Approve-before-push is available via gate:true.)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/argv.mjs
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Small shell-word parser for configured CLI commands. hilos never runs these
|
|
2
|
+
// strings through a shell; this preserves quoted paths/values while keeping the
|
|
3
|
+
// spawn boundary injection-safe. It intentionally supports words, single and
|
|
4
|
+
// double quotes, and backslash-escaped whitespace/quotes—not expansions, pipes,
|
|
5
|
+
// or redirects. Other backslashes stay literal so Windows and UNC paths work.
|
|
6
|
+
|
|
7
|
+
export function commandArgv(command) {
|
|
8
|
+
const input = String(command || "");
|
|
9
|
+
const out = [];
|
|
10
|
+
let word = "";
|
|
11
|
+
let quote = "";
|
|
12
|
+
let started = false;
|
|
13
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
14
|
+
const char = input[index];
|
|
15
|
+
if (char === "\\" && quote !== "'") {
|
|
16
|
+
const next = input[index + 1];
|
|
17
|
+
const escapesQuote = next === '"' || (!quote && next === "'");
|
|
18
|
+
const escapesWhitespace = !quote && typeof next === "string" && /\s/.test(next);
|
|
19
|
+
if (escapesQuote || escapesWhitespace) {
|
|
20
|
+
word += next;
|
|
21
|
+
index += 1;
|
|
22
|
+
} else {
|
|
23
|
+
word += "\\";
|
|
24
|
+
}
|
|
25
|
+
started = true;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (quote) {
|
|
29
|
+
if (char === quote) quote = "";
|
|
30
|
+
else word += char;
|
|
31
|
+
started = true;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (char === "'" || char === '"') {
|
|
35
|
+
quote = char;
|
|
36
|
+
started = true;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (/\s/.test(char)) {
|
|
40
|
+
if (started) {
|
|
41
|
+
out.push(word);
|
|
42
|
+
word = "";
|
|
43
|
+
started = false;
|
|
44
|
+
}
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
word += char;
|
|
48
|
+
started = true;
|
|
49
|
+
}
|
|
50
|
+
if (started) out.push(word);
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Encode one literal argument for `commandArgv`'s small command grammar.
|
|
56
|
+
* Single-quoted spans preserve whitespace and backslashes verbatim; an
|
|
57
|
+
* embedded apostrophe is represented as an adjacent double-quoted span.
|
|
58
|
+
*/
|
|
59
|
+
export function quoteCommandArg(value) {
|
|
60
|
+
return `'${String(value).replaceAll("'", `'"'"'`)}'`;
|
|
61
|
+
}
|