@indigoai-us/hq-cli 5.345.187 → 5.345.188

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,20 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.188] — 2026-10-30
6
+ - `hq agent inbox` and the `hq_inbox_read` MCP tool now bound the server read (token mint plus GET) at 30 s
7
+ (`HQ_AGENT_INBOX_TIMEOUT_MS`, floor 1000) and retry it once after a short delay, honouring the server's
8
+ numeric `Retry-After` up to 5 s, when the token mint is held by another process, the request times out or
9
+ fails in transport, or the server answers 408, 425, 429, 500, 502 or 503. A 504 is left to the existing
10
+ single retry inside the API client, and a 401, 403, 404 or an invalid 200 body is not retried. The
11
+ deadline now reaches the hq-cloud mint, so a retry no longer queues behind a first mint that is still
12
+ waiting on another process's lock. The list command sets `HQ_MACHINE_CREDS_FILE`,
13
+ `HQ_REQUIRE_MACHINE_IDENTITY` and `HQ_VAULT_API_URL` before minting, as `hq agent inbox done` already did.
14
+ The JSON output gains `stale: true|false` next to `source`; whenever a retry happened, including one that
15
+ succeeded, it also carries `attempts: 2`, and a note that still reports a fallback ends with
16
+ "(after 2 attempts)", so automation can tell a hard failure from a single blip (DEV-4065).
17
+ - Keep downloaded revisions current and retry locally deleted files until the cloud confirms their removal.
18
+
5
19
  ## [5.345.187] — 2026-10-30
6
20
  - The HQ Anywhere person gate now follows the desktop Settings key `hq-anywhere-opt-out`. Turning HQ Anywhere on in the app enables `hq mcp serve` without a separate `hq-anywhere` write. Older servers that only have `hq-anywhere` still work.
7
21
 
@@ -8,6 +8,7 @@ import chalk from "chalk";
8
8
  import * as os from "node:os";
9
9
  import { ensureCognitoToken } from "../utils/cognito-session.js";
10
10
  import { vaultApiFetch } from "../utils/vault-api.js";
11
+ import { annotateInboxAttempts, fetchServerInboxWithRetry, parseRetryAfterSeconds, resolveAgentInboxTimeoutMs } from "../lib/agent-kit/inbox-fetch.js";
11
12
  import { redactAgentInboxItem } from "../lib/agent-kit/inbox-redact.js";
12
13
  import { markInboxDone, mergeAgentInbox, readDoneIds, summarizeInboxEntry } from "../lib/agent-kit/inbox-state.js";
13
14
  import { agentKitPaths } from "../lib/agent-kit/paths.js";
@@ -23,25 +24,56 @@ export function registerAgentInboxCommand(agent) {
23
24
  const paths = agentKitPaths(os.homedir(), process.env);
24
25
  const creds = requireExternalCreds(paths);
25
26
  const done = readDoneIds(paths);
27
+ // Pin the enrolled machine identity and API, as `done` does below, so the
28
+ // mint cannot fall through to a person session or another API base.
29
+ process.env.HQ_MACHINE_CREDS_FILE = paths.machineCredsPath;
30
+ process.env.HQ_REQUIRE_MACHINE_IDENTITY = "1";
31
+ process.env.HQ_VAULT_API_URL = creds.apiBaseUrl;
26
32
  // Merged with the server, never the local mirror alone: a dead kit
27
33
  // poller would otherwise make this command answer confidently with
28
- // yesterday's mail and no sign anything was wrong.
29
- const { entries, note, source } = await mergeAgentInbox(paths, async () => {
30
- const token = await ensureCognitoToken({ tokenSource: "machine", interactive: false });
31
- const res = await vaultApiFetch({
32
- token,
33
- baseUrl: creds.apiBaseUrl,
34
- path: `/v1/agents/${encodeURIComponent(creds.entityUid)}/inbox`,
34
+ // yesterday's mail and no sign anything was wrong. The server read is
35
+ // bounded and retried once (inbox-fetch.ts) so a shared-lock mint, a
36
+ // 503 with Retry-After or a hung socket does not become a fallback.
37
+ let attempts = 1;
38
+ const merged = await mergeAgentInbox(paths, async () => {
39
+ const outcome = await fetchServerInboxWithRetry({
40
+ timeoutMs: resolveAgentInboxTimeoutMs(process.env),
41
+ mint: (signal) => ensureCognitoToken({ tokenSource: "machine", interactive: false, signal }),
42
+ fetch: async (token, signal) => {
43
+ const res = await vaultApiFetch({
44
+ token,
45
+ baseUrl: creds.apiBaseUrl,
46
+ path: `/v1/agents/${encodeURIComponent(creds.entityUid)}/inbox`,
47
+ signal,
48
+ });
49
+ const retryAfterSeconds = parseRetryAfterSeconds(res.headers?.get?.("retry-after"));
50
+ // Preserve a decode failure as an invalid body for mergeAgentInbox to
51
+ // classify. Parser errors can contain response bytes: do not echo them.
52
+ return {
53
+ status: res.status,
54
+ body: await res.json().catch(() => undefined),
55
+ ...(retryAfterSeconds !== undefined ? { retryAfterSeconds } : {}),
56
+ };
57
+ },
35
58
  });
36
- // Preserve a decode failure as an invalid body for mergeAgentInbox to
37
- // classify. Parser errors can contain response bytes: do not echo them.
38
- return { status: res.status, body: await res.json().catch(() => undefined) };
59
+ attempts = outcome.attempts;
60
+ if (!outcome.ok)
61
+ throw outcome.error;
62
+ return outcome.response;
39
63
  });
64
+ const { entries, note, source, stale, attempts: attemptsMade } = annotateInboxAttempts(merged, attempts);
40
65
  const rows = entries
41
66
  .filter((e) => opts.all || !done.has(e.id))
42
67
  .map((e) => summarizeInboxEntry(redactAgentInboxItem(e), done.has(e.id)));
43
68
  if (opts.json) {
44
- console.log(JSON.stringify({ count: rows.length, source, messages: rows, ...(note ? { note } : {}) }, null, 2));
69
+ console.log(JSON.stringify({
70
+ count: rows.length,
71
+ source,
72
+ stale,
73
+ ...(attemptsMade !== undefined ? { attempts: attemptsMade } : {}),
74
+ messages: rows,
75
+ ...(note ? { note } : {}),
76
+ }, null, 2));
45
77
  return;
46
78
  }
47
79
  if (note)
@@ -13,6 +13,7 @@ import * as os from "node:os";
13
13
  import { CLI_VERSION } from "../cli-version.js";
14
14
  import { ensureCognitoToken } from "../utils/cognito-session.js";
15
15
  import { getCompanyUid, vaultApiFetch } from "../utils/vault-api.js";
16
+ import { parseRetryAfterSeconds } from "../lib/agent-kit/inbox-fetch.js";
16
17
  import { McpServer } from "../lib/agent-kit/mcp/jsonrpc.js";
17
18
  import { createVaultObjectDownloader } from "../lib/agent-kit/attachment-download.js";
18
19
  import { buildAgentMcpTools } from "../lib/agent-kit/mcp/tools.js";
@@ -59,7 +60,7 @@ export function defaultMcpClients(creds, credsPath, paths = agentKitPaths(os.hom
59
60
  HQ_VAULT_API_URL: creds.apiBaseUrl,
60
61
  NO_COLOR: "1",
61
62
  };
62
- const getToken = () => ensureCognitoToken({ tokenSource: "machine", interactive: false });
63
+ const getToken = (signal) => ensureCognitoToken({ tokenSource: "machine", interactive: false, signal });
63
64
  let companyUid;
64
65
  return {
65
66
  creds,
@@ -89,9 +90,11 @@ export function defaultMcpClients(creds, credsPath, paths = agentKitPaths(os.hom
89
90
  method: init?.method,
90
91
  body: init?.body,
91
92
  query: init?.query,
93
+ signal: init?.signal,
92
94
  });
93
95
  const body = await res.json().catch(() => ({}));
94
- return { status: res.status, body };
96
+ const retryAfterSeconds = parseRetryAfterSeconds(res.headers.get("retry-after"));
97
+ return { status: res.status, body, ...(retryAfterSeconds !== undefined ? { retryAfterSeconds } : {}) };
95
98
  },
96
99
  };
97
100
  }