@indigoai-us/hq-cli 5.117.2 → 5.118.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.118.0] — 2026-09-16
6
+
7
+ ### Added
8
+
9
+ - External agents can now be woken the moment an HQ message arrives. `hq agent kit wake set-url --stdin` points the kit at a webhook, such as a Grok Bot routine webhook trigger. `hq agent kit wake set-command -- <cmd>` runs a local command for frameworks with a CLI. The kit sends a short notice with no message text when mail is pending, and again every 10 minutes until it is handled. The webhook URL is stored privately and never logged.
10
+ - `hq agent inbox` lists an external agent's pending HQ messages and `hq agent inbox done <id>` marks them handled, so a woken bot never answers the same message twice. The MCP server gains a matching `hq_inbox_done` tool.
11
+
12
+ ### Fixed
13
+
14
+ - The `hq_inbox_read` MCP tool no longer fails with 403 AGENT_ROUTE_NOT_SUPPORTED. It reads the agent's own inbox instead of the person-only DM inbox, and by default returns only messages not yet handled.
15
+ - The HQ direct-message skill shipped to external agents no longer tells them to use person-only read commands.
16
+
17
+ ## [5.117.3] — 2026-09-16
18
+
19
+ ### Fixed
20
+
21
+ - `hq doctor --fix` no longer breaks Claude Code sessions. Since HQ moved to a single master hook, the doctor saw every hook script as unregistered. It then added one hook entry per script with no tool filter, so guards such as the Glob path check and core protection ran on every tool call and blocked Bash, Skill and Read with "Glob needs a path" or "Edit to locked path is not allowed". An HQ update could then move those entries into `.claude/settings.local.json`. The doctor no longer auto-registers orphan scripts. It now detects the stray entries in both settings files and `hq doctor --fix` removes them, keeping your permissions and any hooks you added yourself. Scripts listed in `.claude/hooks/hook-registry.json` also no longer show as orphans.
22
+
23
+ ### Fixed
24
+
25
+ - `hq doctor` now keeps its hook-health compatibility verdict in step with
26
+ `check-hq-hooks.sh` when either required command hook is missing.
27
+
5
28
  ## [5.117.2] — 2026-09-16
6
29
 
7
30
  ### Fixed
@@ -26,6 +49,12 @@
26
49
  Workforce now. The free plan is called Starter rather than "free plan", to
27
50
  match the pricing page.
28
51
 
52
+ ### Fixed
53
+
54
+ - `hq doctor` now reports failed hook wiring when `.claude/settings.json` has
55
+ no usable command hook for `SessionStart` or `PreToolUse`. The file can still
56
+ be present and valid JSON, but it no longer reads as healthy when nothing runs.
57
+
29
58
  ## [5.117.1] — 2026-09-16
30
59
 
31
60
  ### Fixed
@@ -4218,6 +4218,83 @@ export declare const COMMAND_CATALOG: readonly [{
4218
4218
  readonly description: "Print machine-readable JSON";
4219
4219
  }];
4220
4220
  readonly subcommands: readonly [];
4221
+ }, {
4222
+ readonly name: "wake";
4223
+ readonly description: "Wake this bot's own framework when HQ messages arrive (webhook URL or local command)";
4224
+ readonly aliases: readonly [];
4225
+ readonly hidden: false;
4226
+ readonly usage: "[options] [command]";
4227
+ readonly arguments: readonly [];
4228
+ readonly options: readonly [];
4229
+ readonly subcommands: readonly [{
4230
+ readonly name: "set-url";
4231
+ readonly description: "POST a notice to a webhook URL (e.g. a Grok Bot routine webhook) when mail is pending. Prefer --stdin so the URL stays out of shell history";
4232
+ readonly aliases: readonly [];
4233
+ readonly hidden: false;
4234
+ readonly usage: "[options] [url]";
4235
+ readonly arguments: readonly [{
4236
+ readonly name: "url";
4237
+ readonly required: false;
4238
+ readonly variadic: false;
4239
+ }];
4240
+ readonly options: readonly [{
4241
+ readonly flags: "--stdin";
4242
+ readonly description: "Read the URL from stdin";
4243
+ }, {
4244
+ readonly flags: "--retry-minutes <n>";
4245
+ readonly description: "Re-wake while items stay pending (default 10, minimum 1)";
4246
+ }];
4247
+ readonly subcommands: readonly [];
4248
+ }, {
4249
+ readonly name: "set-command";
4250
+ readonly description: "Run a local command (no shell) when mail is pending; the notice JSON arrives on stdin. Use -- before the command";
4251
+ readonly aliases: readonly [];
4252
+ readonly hidden: false;
4253
+ readonly usage: "[options] <argv...>";
4254
+ readonly arguments: readonly [{
4255
+ readonly name: "argv";
4256
+ readonly required: true;
4257
+ readonly variadic: true;
4258
+ }];
4259
+ readonly options: readonly [{
4260
+ readonly flags: "--timeout-minutes <n>";
4261
+ readonly description: "Kill a run after this long (default 10)";
4262
+ }, {
4263
+ readonly flags: "--retry-minutes <n>";
4264
+ readonly description: "Re-wake while items stay pending (default 10, minimum 1)";
4265
+ }];
4266
+ readonly subcommands: readonly [];
4267
+ }, {
4268
+ readonly name: "show";
4269
+ readonly description: "Show the configured wake (the URL is masked)";
4270
+ readonly aliases: readonly [];
4271
+ readonly hidden: false;
4272
+ readonly usage: "[options]";
4273
+ readonly arguments: readonly [];
4274
+ readonly options: readonly [{
4275
+ readonly flags: "--json";
4276
+ readonly description: "Print machine-readable JSON";
4277
+ }];
4278
+ readonly subcommands: readonly [];
4279
+ }, {
4280
+ readonly name: "clear";
4281
+ readonly description: "Remove the wake configuration";
4282
+ readonly aliases: readonly [];
4283
+ readonly hidden: false;
4284
+ readonly usage: "[options]";
4285
+ readonly arguments: readonly [];
4286
+ readonly options: readonly [];
4287
+ readonly subcommands: readonly [];
4288
+ }, {
4289
+ readonly name: "test";
4290
+ readonly description: "Fire the wake once now with the current pending count";
4291
+ readonly aliases: readonly [];
4292
+ readonly hidden: false;
4293
+ readonly usage: "[options]";
4294
+ readonly arguments: readonly [];
4295
+ readonly options: readonly [];
4296
+ readonly subcommands: readonly [];
4297
+ }];
4221
4298
  }, {
4222
4299
  readonly name: "run";
4223
4300
  readonly description: "Run one kit service in the foreground (sync|mesh|inbox|heartbeat), or `all` to supervise every service in one process";
@@ -4253,6 +4330,34 @@ export declare const COMMAND_CATALOG: readonly [{
4253
4330
  readonly arguments: readonly [];
4254
4331
  readonly options: readonly [];
4255
4332
  readonly subcommands: readonly [];
4333
+ }, {
4334
+ readonly name: "inbox";
4335
+ readonly description: "List this agent's pending HQ messages (mirrored by the kit), or mark them handled";
4336
+ readonly aliases: readonly [];
4337
+ readonly hidden: false;
4338
+ readonly usage: "[options] [command]";
4339
+ readonly arguments: readonly [];
4340
+ readonly options: readonly [{
4341
+ readonly flags: "--all";
4342
+ readonly description: "Include items already marked handled";
4343
+ }, {
4344
+ readonly flags: "--json";
4345
+ readonly description: "Print machine-readable JSON";
4346
+ }];
4347
+ readonly subcommands: readonly [{
4348
+ readonly name: "done";
4349
+ readonly description: "Mark inbox items handled and ack them on the server";
4350
+ readonly aliases: readonly [];
4351
+ readonly hidden: false;
4352
+ readonly usage: "[options] <ids...>";
4353
+ readonly arguments: readonly [{
4354
+ readonly name: "ids";
4355
+ readonly required: true;
4356
+ readonly variadic: true;
4357
+ }];
4358
+ readonly options: readonly [];
4359
+ readonly subcommands: readonly [];
4360
+ }];
4256
4361
  }];
4257
4362
  }, {
4258
4363
  readonly name: "outposts";
@@ -5457,6 +5457,102 @@ export const COMMAND_CATALOG = [
5457
5457
  ],
5458
5458
  "subcommands": []
5459
5459
  },
5460
+ {
5461
+ "name": "wake",
5462
+ "description": "Wake this bot's own framework when HQ messages arrive (webhook URL or local command)",
5463
+ "aliases": [],
5464
+ "hidden": false,
5465
+ "usage": "[options] [command]",
5466
+ "arguments": [],
5467
+ "options": [],
5468
+ "subcommands": [
5469
+ {
5470
+ "name": "set-url",
5471
+ "description": "POST a notice to a webhook URL (e.g. a Grok Bot routine webhook) when mail is pending. Prefer --stdin so the URL stays out of shell history",
5472
+ "aliases": [],
5473
+ "hidden": false,
5474
+ "usage": "[options] [url]",
5475
+ "arguments": [
5476
+ {
5477
+ "name": "url",
5478
+ "required": false,
5479
+ "variadic": false
5480
+ }
5481
+ ],
5482
+ "options": [
5483
+ {
5484
+ "flags": "--stdin",
5485
+ "description": "Read the URL from stdin"
5486
+ },
5487
+ {
5488
+ "flags": "--retry-minutes <n>",
5489
+ "description": "Re-wake while items stay pending (default 10, minimum 1)"
5490
+ }
5491
+ ],
5492
+ "subcommands": []
5493
+ },
5494
+ {
5495
+ "name": "set-command",
5496
+ "description": "Run a local command (no shell) when mail is pending; the notice JSON arrives on stdin. Use -- before the command",
5497
+ "aliases": [],
5498
+ "hidden": false,
5499
+ "usage": "[options] <argv...>",
5500
+ "arguments": [
5501
+ {
5502
+ "name": "argv",
5503
+ "required": true,
5504
+ "variadic": true
5505
+ }
5506
+ ],
5507
+ "options": [
5508
+ {
5509
+ "flags": "--timeout-minutes <n>",
5510
+ "description": "Kill a run after this long (default 10)"
5511
+ },
5512
+ {
5513
+ "flags": "--retry-minutes <n>",
5514
+ "description": "Re-wake while items stay pending (default 10, minimum 1)"
5515
+ }
5516
+ ],
5517
+ "subcommands": []
5518
+ },
5519
+ {
5520
+ "name": "show",
5521
+ "description": "Show the configured wake (the URL is masked)",
5522
+ "aliases": [],
5523
+ "hidden": false,
5524
+ "usage": "[options]",
5525
+ "arguments": [],
5526
+ "options": [
5527
+ {
5528
+ "flags": "--json",
5529
+ "description": "Print machine-readable JSON"
5530
+ }
5531
+ ],
5532
+ "subcommands": []
5533
+ },
5534
+ {
5535
+ "name": "clear",
5536
+ "description": "Remove the wake configuration",
5537
+ "aliases": [],
5538
+ "hidden": false,
5539
+ "usage": "[options]",
5540
+ "arguments": [],
5541
+ "options": [],
5542
+ "subcommands": []
5543
+ },
5544
+ {
5545
+ "name": "test",
5546
+ "description": "Fire the wake once now with the current pending count",
5547
+ "aliases": [],
5548
+ "hidden": false,
5549
+ "usage": "[options]",
5550
+ "arguments": [],
5551
+ "options": [],
5552
+ "subcommands": []
5553
+ }
5554
+ ]
5555
+ },
5460
5556
  {
5461
5557
  "name": "run",
5462
5558
  "description": "Run one kit service in the foreground (sync|mesh|inbox|heartbeat), or `all` to supervise every service in one process",
@@ -5499,6 +5595,42 @@ export const COMMAND_CATALOG = [
5499
5595
  "arguments": [],
5500
5596
  "options": [],
5501
5597
  "subcommands": []
5598
+ },
5599
+ {
5600
+ "name": "inbox",
5601
+ "description": "List this agent's pending HQ messages (mirrored by the kit), or mark them handled",
5602
+ "aliases": [],
5603
+ "hidden": false,
5604
+ "usage": "[options] [command]",
5605
+ "arguments": [],
5606
+ "options": [
5607
+ {
5608
+ "flags": "--all",
5609
+ "description": "Include items already marked handled"
5610
+ },
5611
+ {
5612
+ "flags": "--json",
5613
+ "description": "Print machine-readable JSON"
5614
+ }
5615
+ ],
5616
+ "subcommands": [
5617
+ {
5618
+ "name": "done",
5619
+ "description": "Mark inbox items handled and ack them on the server",
5620
+ "aliases": [],
5621
+ "hidden": false,
5622
+ "usage": "[options] <ids...>",
5623
+ "arguments": [
5624
+ {
5625
+ "name": "ids",
5626
+ "required": true,
5627
+ "variadic": true
5628
+ }
5629
+ ],
5630
+ "options": [],
5631
+ "subcommands": []
5632
+ }
5633
+ ]
5502
5634
  }
5503
5635
  ]
5504
5636
  },
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `hq agent inbox` — what a woken bot runs to see and clear its HQ mail.
3
+ *
4
+ * hq agent inbox [--all] [--json] pending items (oldest first), or all
5
+ * hq agent inbox done <id...> mark handled locally and ack on the server
6
+ */
7
+ import { Command } from "commander";
8
+ export declare function registerAgentInboxCommand(agent: Command): void;
9
+ //# sourceMappingURL=agent-inbox.d.ts.map
@@ -0,0 +1,81 @@
1
+ /**
2
+ * `hq agent inbox` — what a woken bot runs to see and clear its HQ mail.
3
+ *
4
+ * hq agent inbox [--all] [--json] pending items (oldest first), or all
5
+ * hq agent inbox done <id...> mark handled locally and ack on the server
6
+ */
7
+ import chalk from "chalk";
8
+ import * as os from "node:os";
9
+ import { ensureCognitoToken } from "../utils/cognito-session.js";
10
+ import { vaultApiFetch } from "../utils/vault-api.js";
11
+ import { markInboxDone, readDoneIds, readMirroredInbox, summarizeInboxEntry } from "../lib/agent-kit/inbox-state.js";
12
+ import { agentKitPaths } from "../lib/agent-kit/paths.js";
13
+ import { requireExternalCreds } from "./agent-kit.js";
14
+ const ID_RE = /^[A-Za-z0-9._:-]{1,200}$/;
15
+ export function registerAgentInboxCommand(agent) {
16
+ const inbox = agent
17
+ .command("inbox")
18
+ .description("List this agent's pending HQ messages (mirrored by the kit), or mark them handled")
19
+ .option("--all", "Include items already marked handled")
20
+ .option("--json", "Print machine-readable JSON")
21
+ .action((opts) => {
22
+ const paths = agentKitPaths(os.homedir(), process.env);
23
+ const done = readDoneIds(paths);
24
+ const rows = readMirroredInbox(paths)
25
+ .filter((e) => opts.all || !done.has(e.id))
26
+ .map((e) => summarizeInboxEntry(e, done.has(e.id)));
27
+ if (opts.json) {
28
+ console.log(JSON.stringify({ count: rows.length, messages: rows }, null, 2));
29
+ return;
30
+ }
31
+ if (rows.length === 0) {
32
+ console.log(opts.all ? "Inbox is empty." : "No pending messages.");
33
+ return;
34
+ }
35
+ for (const r of rows) {
36
+ const state = r.done ? chalk.dim(" (done)") : "";
37
+ console.log(`${chalk.bold(r.id)}${state} ${r.channel ?? "?"} from ${r.from}${r.fromUid ? ` <${r.fromUid}>` : ""} ${r.at ?? ""}`);
38
+ if (r.text)
39
+ console.log(` ${r.text.replace(/\n/g, "\n ")}`);
40
+ }
41
+ if (!opts.all)
42
+ console.log(chalk.dim(`\nReply with hq dm <uid> "…", then: hq agent inbox done <id>`));
43
+ });
44
+ inbox
45
+ .command("done <ids...>")
46
+ .description("Mark inbox items handled and ack them on the server")
47
+ .action(async (ids) => {
48
+ const bad = ids.filter((id) => !ID_RE.test(id));
49
+ if (bad.length) {
50
+ console.error(chalk.red(`Invalid id(s): ${bad.join(", ")}`));
51
+ process.exit(1);
52
+ }
53
+ const paths = agentKitPaths(os.homedir(), process.env);
54
+ const creds = requireExternalCreds(paths);
55
+ const added = markInboxDone(paths, ids);
56
+ process.env.HQ_MACHINE_CREDS_FILE = paths.machineCredsPath;
57
+ process.env.HQ_REQUIRE_MACHINE_IDENTITY = "1";
58
+ process.env.HQ_VAULT_API_URL = creds.apiBaseUrl;
59
+ let acked = 0;
60
+ try {
61
+ const token = await ensureCognitoToken({ tokenSource: "machine", interactive: false });
62
+ for (const id of ids) {
63
+ const res = await vaultApiFetch({
64
+ token,
65
+ baseUrl: creds.apiBaseUrl,
66
+ path: `/v1/agents/${encodeURIComponent(creds.entityUid)}/inbox/${encodeURIComponent(id)}/ack`,
67
+ method: "POST",
68
+ });
69
+ if (res.ok)
70
+ acked += 1;
71
+ else
72
+ console.error(chalk.yellow(`server ack ${id} → ${res.status}`));
73
+ }
74
+ }
75
+ catch (err) {
76
+ console.error(chalk.yellow(`server ack skipped: ${err instanceof Error ? err.message : String(err)}`));
77
+ }
78
+ console.log(`Marked ${added.length} handled (${ids.length - added.length} already done); server acked ${acked}.`);
79
+ });
80
+ }
81
+ //# sourceMappingURL=agent-inbox.js.map
@@ -34,6 +34,8 @@ import { fallbackStatus, rebootCrontabLine, shouldUseFallback, startFallback, st
34
34
  import { serviceLogPath } from "../lib/agent-kit/paths.js";
35
35
  import { spawn } from "node:child_process";
36
36
  import { startMeshListener } from "../lib/agent-kit/run/mesh-listener.js";
37
+ import { pendingInbox } from "../lib/agent-kit/inbox-state.js";
38
+ import { buildWakeNotice, clearWakeConfig, currentWakeState, DEFAULT_WAKE_RETRY_MS, describeWake, fireWake, readWakeConfig, validateWakeUrl, writeWakeConfig, } from "../lib/agent-kit/wake.js";
37
39
  import { runSyncLoop } from "../lib/agent-kit/run/sync.js";
38
40
  import { resolveHqBinary, resolveNodeBinary, } from "../lib/mesh/live/daemon/install.js";
39
41
  import { installServices, servicesStatus, uninstallServices, } from "../lib/service-manager/index.js";
@@ -245,6 +247,14 @@ export function formatFallbackStatus(paths, kill) {
245
247
  : chalk.yellow(st.pid === null ? "pidfile unreadable" : `not running (stale pid ${st.pid})`);
246
248
  return [` mode: fallback supervisor (hq agent kit run all) ${state}${chalk.dim(` ${st.pidFile}`)}`];
247
249
  }
250
+ function retryMsFrom(raw) {
251
+ if (raw === undefined)
252
+ return DEFAULT_WAKE_RETRY_MS;
253
+ const n = Number(raw);
254
+ if (!Number.isFinite(n) || n < 1)
255
+ throw new Error("--retry-minutes must be at least 1");
256
+ return n * 60_000;
257
+ }
248
258
  function fail(err) {
249
259
  console.error(chalk.red(err instanceof Error ? err.message : String(err)));
250
260
  process.exit(1);
@@ -341,6 +351,101 @@ export function registerAgentKitCommand(agent) {
341
351
  fail(err);
342
352
  }
343
353
  });
354
+ const wake = kit
355
+ .command("wake")
356
+ .description("Wake this bot's own framework when HQ messages arrive (webhook URL or local command)");
357
+ wake
358
+ .command("set-url [url]")
359
+ .description("POST a notice to a webhook URL (e.g. a Grok Bot routine webhook) when mail is pending. Prefer --stdin so the URL stays out of shell history")
360
+ .option("--stdin", "Read the URL from stdin")
361
+ .option("--retry-minutes <n>", "Re-wake while items stay pending (default 10, minimum 1)")
362
+ .action(async (url, opts) => {
363
+ try {
364
+ const paths = agentKitPaths(os.homedir(), process.env);
365
+ requireExternalCreds(paths);
366
+ let raw = url;
367
+ if (opts.stdin) {
368
+ const chunks = [];
369
+ for await (const c of process.stdin)
370
+ chunks.push(c);
371
+ raw = Buffer.concat(chunks).toString("utf8").trim();
372
+ }
373
+ if (!raw)
374
+ throw new Error("Provide the URL as an argument or with --stdin");
375
+ const config = { kind: "url", url: validateWakeUrl(raw), retryMs: retryMsFrom(opts.retryMinutes) };
376
+ writeWakeConfig(paths, config);
377
+ console.log(chalk.green(`Wake set: ${describeWake(config)}`));
378
+ console.log("Test it: hq agent kit wake test");
379
+ }
380
+ catch (err) {
381
+ fail(err);
382
+ }
383
+ });
384
+ wake
385
+ .command("set-command <argv...>")
386
+ .description("Run a local command (no shell) when mail is pending; the notice JSON arrives on stdin. Use -- before the command")
387
+ .option("--timeout-minutes <n>", "Kill a run after this long (default 10)")
388
+ .option("--retry-minutes <n>", "Re-wake while items stay pending (default 10, minimum 1)")
389
+ .action((argv, opts) => {
390
+ try {
391
+ const paths = agentKitPaths(os.homedir(), process.env);
392
+ requireExternalCreds(paths);
393
+ const minutes = opts.timeoutMinutes ? Number(opts.timeoutMinutes) : 10;
394
+ if (!Number.isFinite(minutes) || minutes <= 0)
395
+ throw new Error("--timeout-minutes must be a positive number");
396
+ const config = { kind: "command", command: argv, timeoutMs: minutes * 60_000, retryMs: retryMsFrom(opts.retryMinutes) };
397
+ writeWakeConfig(paths, config);
398
+ console.log(chalk.green(`Wake set: ${describeWake(config)}`));
399
+ console.log("Test it: hq agent kit wake test");
400
+ }
401
+ catch (err) {
402
+ fail(err);
403
+ }
404
+ });
405
+ wake
406
+ .command("show")
407
+ .description("Show the configured wake (the URL is masked)")
408
+ .option("--json", "Print machine-readable JSON")
409
+ .action((opts) => {
410
+ const paths = agentKitPaths(os.homedir(), process.env);
411
+ const config = readWakeConfig(paths);
412
+ const state = currentWakeState(paths);
413
+ if (opts.json) {
414
+ console.log(JSON.stringify({ configured: !!config, wake: describeWake(config), lastWakeAt: state.lastWakeAt ?? null }, null, 2));
415
+ return;
416
+ }
417
+ console.log(` wake: ${describeWake(config)}`);
418
+ console.log(` last wake: ${state.lastWakeAt ?? "never"}`);
419
+ });
420
+ wake
421
+ .command("clear")
422
+ .description("Remove the wake configuration")
423
+ .action(() => {
424
+ const paths = agentKitPaths(os.homedir(), process.env);
425
+ console.log(clearWakeConfig(paths) ? "Wake cleared." : "No wake was configured.");
426
+ });
427
+ wake
428
+ .command("test")
429
+ .description("Fire the wake once now with the current pending count")
430
+ .action(async () => {
431
+ try {
432
+ const paths = agentKitPaths(os.homedir(), process.env);
433
+ const creds = requireExternalCreds(paths);
434
+ ensureKitDirs(paths);
435
+ const config = readWakeConfig(paths);
436
+ if (!config)
437
+ throw new Error("No wake configured. Run hq agent kit wake set-url --stdin (or set-command).");
438
+ const log = createKitLogger(paths, "wake", { echo: false });
439
+ const ids = pendingInbox(paths).map((e) => e.id);
440
+ const result = await fireWake(config, buildWakeNotice(creds.entityUid, "test", ids, new Date()), { paths, agentUid: creds.entityUid, log });
441
+ console.log(result.ok ? chalk.green(result.detail) : chalk.red(result.detail));
442
+ if (!result.ok)
443
+ process.exit(1);
444
+ }
445
+ catch (err) {
446
+ fail(err);
447
+ }
448
+ });
344
449
  kit
345
450
  .command("run <service>")
346
451
  .description(`Run one kit service in the foreground (${KIT_SERVICES.join("|")}), or \`all\` to supervise every service in one process`)
@@ -11,12 +11,13 @@
11
11
  import { Command } from "commander";
12
12
  import { McpServer } from "../lib/agent-kit/mcp/jsonrpc.js";
13
13
  import { type HqRunResult, type McpToolClients } from "../lib/agent-kit/mcp/tools.js";
14
+ import { type AgentKitPaths } from "../lib/agent-kit/paths.js";
14
15
  import type { ExternalMachineCreds } from "../lib/agent-kit/creds.js";
15
16
  export declare const AGENT_MCP_SERVER_NAME = "hq-agent";
16
17
  export declare const AGENT_MCP_INSTRUCTIONS: string;
17
18
  export declare const HQ_SUBPROCESS_TIMEOUT_MS = 120000;
18
19
  export declare function defaultRunHq(nodeBinary: string, hqBinary: string, env: NodeJS.ProcessEnv): (args: string[]) => Promise<HqRunResult>;
19
- export declare function defaultMcpClients(creds: ExternalMachineCreds, credsPath: string): McpToolClients;
20
+ export declare function defaultMcpClients(creds: ExternalMachineCreds, credsPath: string, paths?: Pick<AgentKitPaths, "inboxDir">): McpToolClients;
20
21
  export declare function createAgentMcpServer(clients: McpToolClients): McpServer;
21
22
  export declare function registerAgentMcpCommand(agent: Command): void;
22
23
  //# sourceMappingURL=agent-mcp.d.ts.map
@@ -21,7 +21,7 @@ export const AGENT_MCP_SERVER_NAME = "hq-agent";
21
21
  export const AGENT_MCP_INSTRUCTIONS = "You are acting inside HQ as an enrolled external agent. Tools run as that machine identity: " +
22
22
  "hq_whoami (identity), hq_search / hq_files_list / hq_files_read (company vault), " +
23
23
  "hq_secrets_list / hq_secrets_exec (secrets by NAME only — values are never returned), " +
24
- "hq_dm_send / hq_inbox_read (direct messages), hq_work_mesh_status (what the team is doing). " +
24
+ "hq_inbox_read / hq_dm_send / hq_inbox_done (read pending HQ messages, reply, then mark each handled), hq_work_mesh_status (what the team is doing). " +
25
25
  "Never ask a user for a token or secret value; if a secret is missing, name it and ask an admin to add it.";
26
26
  export const HQ_SUBPROCESS_TIMEOUT_MS = 120_000;
27
27
  export function defaultRunHq(nodeBinary, hqBinary, env) {
@@ -45,7 +45,7 @@ export function defaultRunHq(nodeBinary, hqBinary, env) {
45
45
  });
46
46
  });
47
47
  }
48
- export function defaultMcpClients(creds, credsPath) {
48
+ export function defaultMcpClients(creds, credsPath, paths = agentKitPaths(os.homedir(), process.env)) {
49
49
  const env = {
50
50
  ...process.env,
51
51
  HQ_MACHINE_CREDS_FILE: credsPath,
@@ -55,6 +55,7 @@ export function defaultMcpClients(creds, credsPath) {
55
55
  };
56
56
  return {
57
57
  creds,
58
+ paths,
58
59
  runHq: defaultRunHq(process.execPath, process.argv[1], env),
59
60
  getToken: () => ensureCognitoToken({ tokenSource: "machine", interactive: false }),
60
61
  apiJson: async (token, path, init) => {
@@ -96,7 +97,7 @@ export function registerAgentMcpCommand(agent) {
96
97
  process.env.HQ_MACHINE_CREDS_FILE = paths.machineCredsPath;
97
98
  process.env.HQ_REQUIRE_MACHINE_IDENTITY = "1";
98
99
  process.env.HQ_VAULT_API_URL = creds.apiBaseUrl;
99
- const server = createAgentMcpServer(defaultMcpClients(creds, paths.machineCredsPath));
100
+ const server = createAgentMcpServer(defaultMcpClients(creds, paths.machineCredsPath, paths));
100
101
  process.stderr.write(`hq agent mcp: serving as ${creds.entityUid} (${creds.companySlug})\n`);
101
102
  await server.serve(process.stdin, process.stdout);
102
103
  });
@@ -6,6 +6,7 @@
6
6
  * hq agent kit install|status|uninstall|run <service>
7
7
  * hq agent probe end-to-end membership check
8
8
  * hq agent mcp stdio MCP server for the bot framework
9
+ * hq agent inbox [done <id…>] pending HQ messages / mark handled
9
10
  */
10
11
  import { Command } from "commander";
11
12
  export declare function registerAgentCommand(program: Command): void;
@@ -6,8 +6,10 @@
6
6
  * hq agent kit install|status|uninstall|run <service>
7
7
  * hq agent probe end-to-end membership check
8
8
  * hq agent mcp stdio MCP server for the bot framework
9
+ * hq agent inbox [done <id…>] pending HQ messages / mark handled
9
10
  */
10
11
  import { registerAgentEnrollCommand } from "./agent-enroll.js";
12
+ import { registerAgentInboxCommand } from "./agent-inbox.js";
11
13
  import { registerAgentKitCommand } from "./agent-kit.js";
12
14
  import { registerAgentMcpCommand } from "./agent-mcp.js";
13
15
  import { registerAgentProbeCommand } from "./agent-probe.js";
@@ -19,5 +21,6 @@ export function registerAgentCommand(program) {
19
21
  registerAgentKitCommand(agent);
20
22
  registerAgentProbeCommand(agent);
21
23
  registerAgentMcpCommand(agent);
24
+ registerAgentInboxCommand(agent);
22
25
  }
23
26
  //# sourceMappingURL=agent.js.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Local handled-state for the external agent's mirrored inbox.
3
+ *
4
+ * The inbox poller appends every new item to ~/.hq-agent/inbox/inbox.jsonl.
5
+ * A bot marks an item handled with `hq agent inbox done <id>` (or the
6
+ * `hq_inbox_done` MCP tool); ids land in done-ids.json. "Pending" is every
7
+ * mirrored item whose id is not done, oldest first. This works the same
8
+ * whether or not the kit acks on the server (`inboxAck`), so a woken bot can
9
+ * always drain exactly what it has not handled yet and never reply twice.
10
+ */
11
+ import type { AgentKitPaths } from "./paths.js";
12
+ export declare const DONE_IDS_NAME = "done-ids.json";
13
+ export declare const MAX_DONE_IDS = 5000;
14
+ export interface MirroredInboxEntry {
15
+ id: string;
16
+ mirroredAt?: string;
17
+ [key: string]: unknown;
18
+ }
19
+ export declare function readDoneIds(paths: Pick<AgentKitPaths, "inboxDir">): Set<string>;
20
+ export declare function markInboxDone(paths: Pick<AgentKitPaths, "inboxDir">, ids: string[]): string[];
21
+ /** Every mirrored entry, oldest first, de-duplicated by id (last write wins). */
22
+ export declare function readMirroredInbox(paths: Pick<AgentKitPaths, "inboxDir">): MirroredInboxEntry[];
23
+ export declare function pendingInbox(paths: Pick<AgentKitPaths, "inboxDir">): MirroredInboxEntry[];
24
+ /** Compact view for bots: who, when, which channel, and the text. */
25
+ export declare function summarizeInboxEntry(e: MirroredInboxEntry, done: boolean): {
26
+ id: string;
27
+ channel: string | undefined;
28
+ from: string;
29
+ fromUid: string | undefined;
30
+ at: string | undefined;
31
+ text: string | undefined;
32
+ done: boolean;
33
+ };
34
+ //# sourceMappingURL=inbox-state.d.ts.map