hilos-agent 0.11.8 → 0.11.10

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.
@@ -30,6 +30,7 @@ import { hookMain, hooksMain } from "../src/hook.mjs";
30
30
  import { runWebMcpCommand } from "../src/webmcp-bridge.mjs";
31
31
  import { detectVendor, fastChatCmd, webCapability } from "../src/progress-emitter.mjs";
32
32
  import { commandArgv } from "../src/argv.mjs";
33
+ import { runWithTerminalSignals } from "../src/cli.mjs";
33
34
 
34
35
  // 1251 — the plugin writes `hook --claude --personal`, so the client flags name
35
36
  // the vendor for `hook` the same way they choose a target for `hooks install`.
@@ -318,7 +319,7 @@ async function main() {
318
319
  delete cliFlags.joinStdin;
319
320
  delete cliFlags.help;
320
321
  const cfg = resolveConfig({ flags: cliFlags, join: joinPayload });
321
- await run(cfg);
322
+ await runWithTerminalSignals((signal) => run(cfg, { signal }));
322
323
  }
323
324
 
324
325
  main().catch((e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hilos-agent",
3
- "version": "0.11.8",
3
+ "version": "0.11.10",
4
4
  "description": "Run your own coding agent (Claude Code, Codex, Cursor, OpenCode, Hermes, or any command) as a teammate in a hilos room. The checkout and credentials stay local; changes go to your configured Git remote as a PR for human review, and bounded progress and reports go to hilos.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,8 +5,15 @@ description: Hand work from this session to the room's agents — one ask, or a
5
5
 
6
6
  # Handoff
7
7
 
8
- Give work to the room instead of doing all of it here. Everything below is
9
- posted as the person running this session; they are the author of record.
8
+ Give work to the room instead of doing all of it here. Two names are involved,
9
+ and the difference matters: the ask is posted as the person running this
10
+ session, so they are the author of record; the work is done by the named agent
11
+ they chose, under that agent's own name. Never present an agent's result as
12
+ the person's, and never let this session do the delegated work itself.
13
+
14
+ Needs the hilos MCP server (`list_room_roster`, `ask_agent`, `propose_plan`).
15
+ If those tools are not in this session, say so and point to
16
+ https://hilos.sh/docs/share-from-your-terminal.
10
17
 
11
18
  1. Call `list_room_roster` for the room. Offer the agents it returns as the
12
19
  options, with their state, so nobody hands work to an agent already busy.
@@ -8,6 +8,10 @@ description: Show who is in the hilos room this session is attached to, and what
8
8
  Who is at the table right now, and what is waiting on the person running this
9
9
  session. Read-only: this skill never posts anything.
10
10
 
11
+ Needs the hilos MCP server (`list_channels`, `list_room_roster`,
12
+ `list_mentions`). If those tools are not in this session, say so and point to
13
+ https://hilos.sh/docs/share-from-your-terminal; never invent a roster.
14
+
11
15
  1. Find the room. Use the channel this session has already posted into. If
12
16
  there is none, call `list_channels` and ask which room to read.
13
17
  2. Call `list_room_roster` with that channel id.
@@ -6,7 +6,12 @@ description: Post a short summary, a diff, or a link from this session into the
6
6
  # Share
7
7
 
8
8
  Put what just happened in front of the people in the room. The message is
9
- posted as the person running this session, under their name.
9
+ posted as the person running this session, under their name, with a small
10
+ "via Claude Code" (or Codex, Cursor) marker.
11
+
12
+ Needs the hilos MCP server (`list_channels`, `post_message`). If those tools
13
+ are not in this session, say so and point to
14
+ https://hilos.sh/docs/share-from-your-terminal instead of pretending to post.
10
15
 
11
16
  1. Find the thread. Use the thread this session last posted into. If there is
12
17
  none, call `list_channels`, ask which room, and post at top level there.
package/src/cli.mjs CHANGED
@@ -7,6 +7,30 @@
7
7
 
8
8
  import { spawn } from "node:child_process";
9
9
 
10
+ /**
11
+ * Let the daemon finish its existing abort/cleanup path before exiting.
12
+ * @param {(signal: AbortSignal) => Promise<any>} task
13
+ * @param {import("node:events").EventEmitter & { exitCode?: number | string }} signalSource
14
+ */
15
+ export async function runWithTerminalSignals(task, signalSource = process) {
16
+ const controller = new AbortController();
17
+ const interrupt = () => stop("SIGINT", 130);
18
+ const terminate = () => stop("SIGTERM", 143);
19
+ const stop = (name, code) => {
20
+ if (controller.signal.aborted) return;
21
+ signalSource.exitCode = code;
22
+ controller.abort(new Error(`Daemon received ${name}`));
23
+ };
24
+ signalSource.once("SIGINT", interrupt);
25
+ signalSource.once("SIGTERM", terminate);
26
+ try {
27
+ return await task(controller.signal);
28
+ } finally {
29
+ signalSource.removeListener("SIGINT", interrupt);
30
+ signalSource.removeListener("SIGTERM", terminate);
31
+ }
32
+ }
33
+
10
34
  // ── Environment isolation ────────────────────────────────────────────────────
11
35
  // The coding/chat CLI we spawn (`claude -p`, `codex`, …) is a model with tool
12
36
  // use: it can run `env`/`printenv` and echo whatever it sees into the channel.
package/src/handler.mjs CHANGED
@@ -4975,6 +4975,11 @@ export async function handleTask({ message, channelId, tool, me, caps = {}, iter
4975
4975
  body,
4976
4976
  });
4977
4977
  await updateChannelMarker(compactRunMarker("cancelled", branch));
4978
+ // 1270 — a process interruption has no server-side Stop action to settle
4979
+ // the row. Iterate claims keep their fenced recovery path in run().
4980
+ if (runId && caps.runs && !iterateClaimId) {
4981
+ await tool("update_run", { runId, status: "failed", reason: "daemon-stopped" }).catch(() => {});
4982
+ }
4978
4983
  // Every repo-lane cancel funnels through here, so one settle covers them
4979
4984
  // all: a stopped run still spent tokens (0787).
4980
4985
  await settleRunUsage();
@@ -5254,8 +5259,19 @@ export async function handleTask({ message, channelId, tool, me, caps = {}, iter
5254
5259
  compactRunMarker(staged.failed ? "run-failed" : "no-changes", branch),
5255
5260
  );
5256
5261
  await settleRunUsage(); // "no changes" is not "no spend" (0787)
5257
- // 1182 — no turn is left in this pass: what is held is answered now.
5262
+ // 1182 — retry direction receipts before terminal settlement drops any
5263
+ // still-unacknowledged input. No turn is left in this pass.
5258
5264
  if (directions) await directions.close();
5265
+ // A terminal progress card is presentation; it does not settle this row.
5266
+ // Keep an existing PR at its review gate when feedback produces no diff.
5267
+ if (runId && caps.runs && !iterateClaimId) {
5268
+ await tool("update_run", {
5269
+ runId,
5270
+ status: continuingPrUrl ? "awaiting_review" : staged.failed ? "failed" : "succeeded",
5271
+ ...(continuingPrUrl ? { prUrl: continuingPrUrl } : {}),
5272
+ ...(!continuingPrUrl ? { reason: staged.failed ? "coding-tool-failed" : "no-changes" } : {}),
5273
+ });
5274
+ }
5259
5275
  return { status: staged.failed ? "run-failed" : "no-changes" };
5260
5276
  };
5261
5277
  if (staged.empty) return await finishEmptyTree({ afterDirection: afterCoding.advanced });