@indigoai-us/hq-cli 5.140.0 → 5.141.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,10 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.141.0] — 2026-09-23
6
+
7
+ - `hq bot jobs` now checks the bot's configured agent ID against the machine credential before calling the jobs API. A mismatch reports both IDs and explains how to fix the bot configuration or re-mint its credentials.
8
+
5
9
  ## [5.140.0] — 2026-09-23
6
10
 
7
11
  ### Fixed
@@ -20,6 +20,7 @@ import * as fs from "node:fs";
20
20
  import * as path from "node:path";
21
21
  import { BotApi, BotApiError } from "../lib/bot/api.js";
22
22
  import { effectiveBotKind } from "../lib/bot/config.js";
23
+ import { identityMismatchMessage, tokenAgentUid } from "../lib/heartbeat-identity.js";
23
24
  // ─── Schedule translation ────────────────────────────────────────────────────
24
25
  /** Mirrors hq-pro-agents `MIN_RECURRING_INTERVAL_SECONDS`. */
25
26
  export const MIN_RECURRING_INTERVAL_SECONDS = 5 * 60;
@@ -363,10 +364,19 @@ export async function runBotJobs(nameArg, opts, deps) {
363
364
  const { name, dir, config } = deps.requireBot(nameArg);
364
365
  const kind = effectiveBotKind(config);
365
366
  const out = deps.out ?? ((line) => console.log(line));
366
- const api = deps.apiFor
367
- ? deps.apiFor(dir)
368
- : new BotApi({ token: deps.botTokenSupplier(dir) });
369
367
  const ctx = { botName: name, kind };
368
+ let client;
369
+ const jobsApi = async () => {
370
+ if (client)
371
+ return client;
372
+ const token = await deps.botTokenSupplier(dir)();
373
+ const tokenUid = tokenAgentUid(token);
374
+ if (tokenUid && tokenUid !== config.agentUid) {
375
+ refuse(identityMismatchMessage("bot jobs", config.agentUid, tokenUid));
376
+ }
377
+ client = deps.apiFor ? deps.apiFor(dir) : new BotApi({ token: async () => token });
378
+ return client;
379
+ };
370
380
  if (opts.add !== undefined && opts.rm !== undefined) {
371
381
  refuse("Add a job or remove one, not both in the same command.");
372
382
  }
@@ -374,6 +384,7 @@ export async function runBotJobs(nameArg, opts, deps) {
374
384
  const jobId = opts.rm.trim();
375
385
  if (!jobId)
376
386
  refuse("Say which job to remove: --rm <jobId>");
387
+ const api = await jobsApi();
377
388
  try {
378
389
  await api.cancelJob(config.agentUid, jobId);
379
390
  }
@@ -395,6 +406,7 @@ export async function runBotJobs(nameArg, opts, deps) {
395
406
  ? parse(opts.every, { hqRoot: config.hqRoot, timezone })
396
407
  : { cron: opts.cron, timezone };
397
408
  const body = buildCreateJobBody({ prompt: opts.add, cron: schedule.cron, timezone: schedule.timezone });
409
+ const api = await jobsApi();
398
410
  let created;
399
411
  try {
400
412
  created = await api.createJob(config.agentUid, body);
@@ -414,6 +426,7 @@ export async function runBotJobs(nameArg, opts, deps) {
414
426
  return;
415
427
  }
416
428
  let jobs;
429
+ const api = await jobsApi();
417
430
  try {
418
431
  jobs = await api.listJobs(config.agentUid);
419
432
  }
@@ -14,7 +14,7 @@
14
14
  import * as fs from "node:fs";
15
15
  import * as os from "node:os";
16
16
  import { CLI_VERSION } from "../../../cli-version.js";
17
- import { captureHeartbeatIdentityMismatch, isHeartbeatIdentityMismatch, parseHeartbeatFailureBody, tokenAgentUid, } from "../../heartbeat-identity.js";
17
+ import { captureHeartbeatIdentityMismatch, identityMismatchMessage, isHeartbeatIdentityMismatch, parseHeartbeatFailureBody, tokenAgentUid, } from "../../heartbeat-identity.js";
18
18
  import { vaultApiFetch } from "../../../utils/vault-api.js";
19
19
  import { readComponentStatus } from "../creds.js";
20
20
  import { KIT_COMPONENTS } from "../paths.js";
@@ -90,7 +90,7 @@ async function beatOnceResult(deps) {
90
90
  ...(tokenUid ? { tokenAgentUid: tokenUid } : {}),
91
91
  });
92
92
  const tokenLabel = tokenUid ?? "unknown";
93
- deps.log("warn", `heartbeat stopped: identity mismatch configured agentUid=${deps.agentUid} token agentUid=${tokenLabel}`);
93
+ deps.log("warn", identityMismatchMessage("heartbeat", deps.agentUid, tokenLabel));
94
94
  (deps.reportIdentityMismatch ?? captureHeartbeatIdentityMismatch)({
95
95
  runner: "agent-kit",
96
96
  configuredAgentUid: deps.agentUid,
@@ -69,7 +69,7 @@ import { buildJobTurnPrompt, isCatchUpRun, isJobItem, jobReportHeader, jobRunOut
69
69
  import { clearPendingJobCompletion, isCompletionDue, isCompletionExpired, isCompletionForOtherAgent, readPendingJobCompletions, recordPendingJobCompletion } from "./job-completions.js";
70
70
  import { isRuntimeSignInFailure, SIGN_IN_RETRY_MS, signInNeededText } from "./runtime-sign-in.js";
71
71
  import { HQ_BOT_AGENT_UID_ENV } from "../../commands/onboard-bot-membership.js";
72
- import { captureHeartbeatIdentityMismatch, isHeartbeatIdentityMismatch, } from "../heartbeat-identity.js";
72
+ import { captureHeartbeatIdentityMismatch, identityMismatchMessage, isHeartbeatIdentityMismatch, } from "../heartbeat-identity.js";
73
73
  import { clearBotSession, readBotSession, writeBotSession } from "./session.js";
74
74
  import { defaultBotStatus, patchBotStatus, readBotStatus } from "./status.js";
75
75
  export const INBOX_POLL_INTERVAL_MS = 2_000;
@@ -459,7 +459,7 @@ export async function runBot(deps) {
459
459
  if (err instanceof BotApiError &&
460
460
  isHeartbeatIdentityMismatch({ status: err.status, code: err.code, message: err.detail })) {
461
461
  const tokenLabel = err.tokenAgentUid ?? "unknown";
462
- const reason = `heartbeat stopped: identity mismatch configured agentUid=${config.agentUid} token agentUid=${tokenLabel}`;
462
+ const reason = identityMismatchMessage("heartbeat", config.agentUid, tokenLabel);
463
463
  setStatus({
464
464
  lastHeartbeatOk: false,
465
465
  lastHeartbeatError: reason.slice(0, 400),
@@ -1,5 +1,6 @@
1
1
  import type { CaptureContext } from "@sentry/node";
2
2
  export declare const HEARTBEAT_IDENTITY_MISMATCH_CODE = "AGENT_IDENTITY_MISMATCH";
3
+ export declare function identityMismatchMessage(surface: "heartbeat" | "bot jobs", configuredAgentUid: string, tokenAgentUid?: string): string;
3
4
  export type HeartbeatRunner = "agent-kit" | "bot";
4
5
  export type HeartbeatAgentUidSource = "config_file" | "env" | "flag";
5
6
  export type HeartbeatCredsSource = "explicit" | "default";
@@ -2,6 +2,11 @@ import { CLI_VERSION } from "../cli-version.js";
2
2
  import { Sentry } from "../sentry.js";
3
3
  import { peekIdToken } from "../utils/id-token.js";
4
4
  export const HEARTBEAT_IDENTITY_MISMATCH_CODE = "AGENT_IDENTITY_MISMATCH";
5
+ export function identityMismatchMessage(surface, configuredAgentUid, tokenAgentUid) {
6
+ return (`${surface} stopped: identity mismatch configured agentUid=${configuredAgentUid} ` +
7
+ `token agentUid=${tokenAgentUid ?? "unknown"}; fix the bot config to use the token agentUid, ` +
8
+ `or re-mint credentials for the configured agentUid.`);
9
+ }
5
10
  const AGENT_UID_RE = /^agt_[A-Za-z0-9_-]{1,100}$/;
6
11
  function normalize(value) {
7
12
  return (value ?? "").replace(/[^A-Za-z0-9]/g, "").toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.140.0",
3
+ "version": "5.141.0",
4
4
  "description": "HQ by Indigo management CLI \u2014 modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {