@indigoai-us/hq-cli 5.115.6 → 5.117.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.
Files changed (76) hide show
  1. package/CHANGELOG.md +169 -11
  2. package/dist/command-catalog.generated.d.ts +220 -2
  3. package/dist/command-catalog.generated.js +281 -2
  4. package/dist/command-registration-plan.d.ts +6 -0
  5. package/dist/command-registration-plan.js +1 -0
  6. package/dist/commands/agent-enroll.d.ts +105 -0
  7. package/dist/commands/agent-enroll.js +273 -0
  8. package/dist/commands/agent-kit.d.ts +53 -0
  9. package/dist/commands/agent-kit.js +260 -0
  10. package/dist/commands/agent-mcp.d.ts +22 -0
  11. package/dist/commands/agent-mcp.js +104 -0
  12. package/dist/commands/agent-probe.d.ts +71 -0
  13. package/dist/commands/agent-probe.js +294 -0
  14. package/dist/commands/agent.d.ts +12 -0
  15. package/dist/commands/agent.js +23 -0
  16. package/dist/commands/agents.d.ts +27 -0
  17. package/dist/commands/agents.js +280 -6
  18. package/dist/commands/bot.d.ts +140 -1
  19. package/dist/commands/bot.js +757 -22
  20. package/dist/commands/secrets.js +17 -5
  21. package/dist/lib/agent-kit/creds.d.ts +60 -0
  22. package/dist/lib/agent-kit/creds.js +123 -0
  23. package/dist/lib/agent-kit/kit-config.d.ts +29 -0
  24. package/dist/lib/agent-kit/kit-config.js +54 -0
  25. package/dist/lib/agent-kit/log.d.ts +17 -0
  26. package/dist/lib/agent-kit/log.js +46 -0
  27. package/dist/lib/agent-kit/mcp/jsonrpc.d.ts +84 -0
  28. package/dist/lib/agent-kit/mcp/jsonrpc.js +164 -0
  29. package/dist/lib/agent-kit/mcp/tools.d.ts +45 -0
  30. package/dist/lib/agent-kit/mcp/tools.js +280 -0
  31. package/dist/lib/agent-kit/paths.d.ts +42 -0
  32. package/dist/lib/agent-kit/paths.js +56 -0
  33. package/dist/lib/agent-kit/run/heartbeat.d.ts +52 -0
  34. package/dist/lib/agent-kit/run/heartbeat.js +97 -0
  35. package/dist/lib/agent-kit/run/inbox.d.ts +59 -0
  36. package/dist/lib/agent-kit/run/inbox.js +152 -0
  37. package/dist/lib/agent-kit/run/mesh-listener.d.ts +58 -0
  38. package/dist/lib/agent-kit/run/mesh-listener.js +193 -0
  39. package/dist/lib/agent-kit/run/sync.d.ts +33 -0
  40. package/dist/lib/agent-kit/run/sync.js +58 -0
  41. package/dist/lib/agent-kit/services.d.ts +21 -0
  42. package/dist/lib/agent-kit/services.js +46 -0
  43. package/dist/lib/agent-kit/skills.d.ts +18 -0
  44. package/dist/lib/agent-kit/skills.js +149 -0
  45. package/dist/lib/bot/api.d.ts +51 -0
  46. package/dist/lib/bot/api.js +32 -0
  47. package/dist/lib/bot/daemon.d.ts +17 -0
  48. package/dist/lib/bot/daemon.js +44 -3
  49. package/dist/lib/bot/index.d.ts +4 -0
  50. package/dist/lib/bot/index.js +4 -0
  51. package/dist/lib/bot/inflight.d.ts +14 -0
  52. package/dist/lib/bot/local-config.d.ts +70 -0
  53. package/dist/lib/bot/local-config.js +147 -0
  54. package/dist/lib/bot/local-name.d.ts +54 -0
  55. package/dist/lib/bot/local-name.js +114 -0
  56. package/dist/lib/bot/run.d.ts +9 -0
  57. package/dist/lib/bot/run.js +117 -24
  58. package/dist/lib/bot/runnable.d.ts +51 -0
  59. package/dist/lib/bot/runnable.js +65 -0
  60. package/dist/lib/bot/self-heal.d.ts +52 -0
  61. package/dist/lib/bot/self-heal.js +79 -0
  62. package/dist/lib/bot/split.d.ts +32 -0
  63. package/dist/lib/bot/split.js +241 -0
  64. package/dist/lib/service-manager/index.d.ts +43 -0
  65. package/dist/lib/service-manager/index.js +114 -0
  66. package/dist/lib/service-manager/launchd.d.ts +23 -0
  67. package/dist/lib/service-manager/launchd.js +81 -0
  68. package/dist/lib/service-manager/systemd.d.ts +19 -0
  69. package/dist/lib/service-manager/systemd.js +72 -0
  70. package/dist/lib/service-manager/types.d.ts +32 -0
  71. package/dist/lib/service-manager/types.js +26 -0
  72. package/dist/utils/self-update.js +2 -30
  73. package/dist/utils/update-command-supervisor.cjs +194 -0
  74. package/dist/utils/version-gate.d.ts +18 -0
  75. package/dist/utils/version-gate.js +126 -7
  76. package/package.json +2 -2
@@ -15,6 +15,8 @@
15
15
  * hq agents start|stop <uid> — EC2 start/stop
16
16
  * hq agents retry <uid> — resume setup from first non-done step
17
17
  * hq agents rm <uid> --yes — deprovision (destructive; flag-guarded)
18
+ * hq agents rotate <uid> — new enrollment code for an external agent
19
+ * hq agents revoke <uid> --yes — revoke an external agent (--remove deletes it)
18
20
  * hq agents jobs list <uid> — off-box job roster (schedule + rate)
19
21
  * hq agents jobs pause <uid> <jobId> — flip schedule State=DISABLED
20
22
  * hq agents jobs cancel <uid> <jobId> — delete schedule + drop the job
@@ -387,6 +389,143 @@ export async function getAgentStatus(token, agentUid) {
387
389
  path: `/v1/agents/${encodeURIComponent(agentUid)}/status`,
388
390
  });
389
391
  }
392
+ function recordValue(value) {
393
+ return value && typeof value === "object" && !Array.isArray(value)
394
+ ? value
395
+ : null;
396
+ }
397
+ function nonEmptyString(value) {
398
+ return typeof value === "string" && value.trim() ? value.trim() : null;
399
+ }
400
+ function safeActionUrl(value) {
401
+ const raw = nonEmptyString(value);
402
+ if (!raw)
403
+ return null;
404
+ try {
405
+ const url = new URL(raw);
406
+ return url.protocol === "https:" && !url.username && !url.password ? raw : null;
407
+ }
408
+ catch {
409
+ return null;
410
+ }
411
+ }
412
+ function signInProviderName(url) {
413
+ const parsed = new URL(url);
414
+ if (parsed.hostname === "auth.openai.com" &&
415
+ parsed.pathname.startsWith("/codex/")) {
416
+ return "Codex";
417
+ }
418
+ if (parsed.hostname === "accounts.x.ai")
419
+ return "Grok";
420
+ if (parsed.hostname.endsWith("anthropic.com"))
421
+ return "Claude";
422
+ return "your provider";
423
+ }
424
+ function currentDeviceSignInAction(status) {
425
+ const pairing = recordValue(status.pairing);
426
+ const url = safeActionUrl(pairing?.url);
427
+ if (!url)
428
+ return null;
429
+ const rawCode = nonEmptyString(pairing?.code);
430
+ const code = rawCode && /^[A-Z0-9]{4,8}-[A-Z0-9]{4,8}$/i.test(rawCode)
431
+ ? rawCode
432
+ : undefined;
433
+ const provider = signInProviderName(url);
434
+ return {
435
+ type: provider === "Codex" ? "codex-device-sign-in" : "provider-sign-in",
436
+ title: `Sign in to ${provider}`,
437
+ summary: `sign in to ${provider}`,
438
+ instruction: code
439
+ ? "Open this page and enter the code."
440
+ : "Open this page and follow the sign-in instructions.",
441
+ url,
442
+ urlLabel: "Open",
443
+ code,
444
+ };
445
+ }
446
+ function pendingSlackAction(status) {
447
+ const agent = recordValue(status.agent);
448
+ const diagnostics = recordValue(agent?.channelDiagnostics);
449
+ const slack = recordValue(diagnostics?.slack);
450
+ const url = safeActionUrl(slack?.pendingInstallUrl);
451
+ if (!url)
452
+ return null;
453
+ if (slack?.inboundCapability === "socket-mode-degraded") {
454
+ return {
455
+ type: "slack-app-token",
456
+ title: "Finish setting up the Slack app",
457
+ summary: "finish setting up the Slack app",
458
+ instruction: "Open this page, create the app-level token, then provide it in the agent's Slack setup.",
459
+ url,
460
+ urlLabel: "Open",
461
+ };
462
+ }
463
+ if (slack?.inboundCapability !== "pending-install")
464
+ return null;
465
+ return {
466
+ type: "slack-install",
467
+ title: "Install the Slack app",
468
+ summary: "install the Slack app so it can talk to your workspace",
469
+ instruction: "Open this link and install the app in your workspace.",
470
+ url,
471
+ urlLabel: "Install",
472
+ };
473
+ }
474
+ /**
475
+ * The server owns action detection. This client converts its safe, current
476
+ * status fields into a list so another operator action can be added without
477
+ * changing the renderers or command control flow.
478
+ */
479
+ function pendingOperatorActions(status) {
480
+ return [
481
+ currentDeviceSignInAction(status),
482
+ pendingSlackAction(status),
483
+ ].filter((action) => action !== null);
484
+ }
485
+ function pendingActionsJson(actions) {
486
+ return actions.map(({ type, title, instruction, url, code }) => ({
487
+ type,
488
+ title,
489
+ instruction,
490
+ ...(url ? { url } : {}),
491
+ ...(code ? { code } : {}),
492
+ }));
493
+ }
494
+ function statusAgentSlug(status) {
495
+ return nonEmptyString(recordValue(status.agent)?.slug);
496
+ }
497
+ /** The provision endpoint returns this nested identity; old response shapes omit it. */
498
+ function provisionedAgentIdentity(result) {
499
+ const agent = recordValue(result.agent);
500
+ const uid = nonEmptyString(agent?.uid) ?? nonEmptyString(agent?.agentUid);
501
+ const slug = nonEmptyString(agent?.slug);
502
+ return uid && slug ? { uid, slug } : null;
503
+ }
504
+ function printPendingOperatorActions(slug, actions) {
505
+ if (actions.length === 0)
506
+ return;
507
+ const first = actions[0];
508
+ console.log(chalk.yellow(actions.length === 1
509
+ ? `Your agent's box is up. One step left: ${first.summary}.`
510
+ : "Your agent's box is up. A few steps remain before it can talk to your workspace."));
511
+ for (const action of actions) {
512
+ console.log("");
513
+ console.log(chalk.bold(`${action.title}:`));
514
+ console.log(action.instruction);
515
+ if (action.url)
516
+ console.log(chalk.cyan(`${action.urlLabel ?? "Open"}: ${action.url}`));
517
+ if (action.code)
518
+ console.log(chalk.bold(`Code: ${action.code}`));
519
+ }
520
+ const currentCodeHint = actions.some((action) => action.code)
521
+ ? "current sign-in code and instructions"
522
+ : "these instructions";
523
+ const statusCommand = slug
524
+ ? `Run \`hq agents status ${slug}\` any time to get ${currentCodeHint} again.`
525
+ : `Run \`hq agents status\` again any time to get ${currentCodeHint}.`;
526
+ console.log("");
527
+ console.log(chalk.dim(`Until that's done, the agent will show as still setting up. ${statusCommand}`));
528
+ }
390
529
  export async function patchAgentProfile(token, agentUid, patch) {
391
530
  return agentsRequest({
392
531
  token,
@@ -656,6 +795,59 @@ function shortTime(iso) {
656
795
  function sleep(ms) {
657
796
  return new Promise((resolve) => setTimeout(resolve, ms));
658
797
  }
798
+ /**
799
+ * POST /v1/agents/{uid}/rotate — invalidate the current secret and host key
800
+ * and issue a fresh one-time enrollment code (returned exactly once).
801
+ */
802
+ export async function rotateAgent(token, agentUid) {
803
+ const data = await agentsRequest({
804
+ token,
805
+ path: `/v1/agents/${encodeURIComponent(agentUid)}/rotate`,
806
+ method: "POST",
807
+ });
808
+ const e = data.enrollment ?? {};
809
+ if (typeof e.code !== "string" || !e.code) {
810
+ throw new AgentsHttpError(502, "rotate succeeded but the response carried no enrollment code");
811
+ }
812
+ return {
813
+ agentUid: data.agentUid ?? agentUid,
814
+ enrollment: {
815
+ code: e.code,
816
+ expiresAt: typeof e.expiresAt === "string" ? e.expiresAt : "",
817
+ enrollmentId: typeof e.enrollmentId === "string" ? e.enrollmentId : "",
818
+ },
819
+ };
820
+ }
821
+ /** Display form of a 24-char code: six groups of four. */
822
+ export function groupEnrollmentCode(code) {
823
+ const flat = code.replace(/[\s-]+/g, "").toUpperCase();
824
+ return flat.match(/.{1,4}/g)?.join("-") ?? flat;
825
+ }
826
+ /**
827
+ * POST /v1/agents/{uid}/revoke — disable the Cognito user, drop membership,
828
+ * mark the enrollment revoked; `remove` also deletes the entity.
829
+ */
830
+ export async function revokeAgent(token, agentUid, remove) {
831
+ const data = await agentsRequest({
832
+ token,
833
+ path: `/v1/agents/${encodeURIComponent(agentUid)}/revoke`,
834
+ method: "POST",
835
+ body: { remove },
836
+ });
837
+ return { removed: data.removed === true || remove };
838
+ }
839
+ /** Lines printed after a rotate: the code is shown once and never stored. */
840
+ export function formatRotateOutput(result) {
841
+ const lines = [
842
+ `Rotated ${result.agentUid}. The old secret and host key no longer mint.`,
843
+ "",
844
+ ` enrollment code: ${groupEnrollmentCode(result.enrollment.code)}`,
845
+ ];
846
+ if (result.enrollment.expiresAt)
847
+ lines.push(` expires: ${result.enrollment.expiresAt} (15 minutes)`);
848
+ lines.push("", "Shown once. On the agent's host run:", ` hq agent enroll ${groupEnrollmentCode(result.enrollment.code)} --replace`, " hq agent probe");
849
+ return lines;
850
+ }
659
851
  // ---------------------------------------------------------------------------
660
852
  // Command registration
661
853
  // ---------------------------------------------------------------------------
@@ -711,6 +903,11 @@ export function registerAgentsCommand(program) {
711
903
  // Interactive terminals are for humans: hq-pro serves no keyed
712
904
  // `/v1/keys/agents/{uid}/terminal` route, so machine keys fail closed.
713
905
  terminal: { capability: "agents:use", routeAvailable: false },
906
+ // External-agent lifecycle is an admin action on the JWT-only
907
+ // /v1/agents/{uid}/rotate|revoke routes (api-contract v1); no keyed
908
+ // parallel exists, so machine keys fail closed here too.
909
+ rotate: { capability: "agents:use", routeAvailable: false },
910
+ revoke: { capability: "agents:use", routeAvailable: false },
714
911
  },
715
912
  });
716
913
  // `--company` may live on the group or the subcommand; the subcommand value
@@ -955,13 +1152,34 @@ export function registerAgentsCommand(program) {
955
1152
  quoteCatalogVersion: createOptions.catalogVersion,
956
1153
  surface: CLI_AGENT_CREATE_SURFACE,
957
1154
  });
958
- const uid = typeof result.uid === "string" ? result.uid : slug;
1155
+ const identity = provisionedAgentIdentity(result);
1156
+ const status = identity
1157
+ ? await getAgentStatus(token, identity.uid)
1158
+ : null;
1159
+ const actions = status ? pendingOperatorActions(status) : [];
1160
+ const slackInstall = actions.find((action) => action.type === "slack-install");
959
1161
  if (opts.json) {
960
- console.log(JSON.stringify(result));
1162
+ console.log(JSON.stringify({
1163
+ ...result,
1164
+ ...(actions.length > 0
1165
+ ? { pendingActions: pendingActionsJson(actions) }
1166
+ : {}),
1167
+ ...(slackInstall?.url
1168
+ ? {
1169
+ slackInstallPending: true,
1170
+ slackInstallUrl: slackInstall.url,
1171
+ }
1172
+ : {}),
1173
+ }));
961
1174
  }
962
1175
  else {
963
1176
  console.log(chalk.green(`Provisioning started for agent "${name}".`));
964
- console.log(chalk.dim(`Track setup: hq agents status ${uid} --company <slug>`));
1177
+ if (actions.length > 0) {
1178
+ printPendingOperatorActions(identity?.slug ?? null, actions);
1179
+ }
1180
+ else {
1181
+ console.log(chalk.dim(`Track setup: hq agents status ${identity?.slug ?? slug} --company <slug>`));
1182
+ }
965
1183
  }
966
1184
  }
967
1185
  catch (err) {
@@ -1050,16 +1268,28 @@ export function registerAgentsCommand(program) {
1050
1268
  }
1051
1269
  });
1052
1270
  agents
1053
- .command("status <agentUid>")
1271
+ .command("status <agent>")
1054
1272
  .description("Show an agent's setup state and runtime detail")
1055
1273
  .option("--company <slug>", "Company slug (resolves to companyUid)")
1056
1274
  .option("--json", "Emit raw JSON")
1057
- .action(async function (agentUid, opts) {
1275
+ .action(async function (agent, opts) {
1058
1276
  try {
1059
1277
  const token = (await resolveVaultCredential()).token;
1278
+ const agentUid = await resolveAgentUid(token, agent, companyOf(this));
1060
1279
  const status = await getAgentStatus(token, agentUid);
1280
+ const actions = pendingOperatorActions(status);
1061
1281
  if (opts.json) {
1062
- process.stdout.write(JSON.stringify(status, null, 2) + "\n");
1282
+ process.stdout.write(JSON.stringify({
1283
+ ...status,
1284
+ ...(actions.length > 0
1285
+ ? { pendingActions: pendingActionsJson(actions) }
1286
+ : {}),
1287
+ }, null, 2) + "\n");
1288
+ return;
1289
+ }
1290
+ if (actions.length > 0) {
1291
+ printPendingOperatorActions(statusAgentSlug(status) ??
1292
+ (AGENT_UID_PATTERN.test(agent.trim()) ? null : agent.trim()), actions);
1063
1293
  return;
1064
1294
  }
1065
1295
  // Pretty but faithful: dump the top-level fields as key: value.
@@ -1268,6 +1498,50 @@ export function registerAgentsCommand(program) {
1268
1498
  fail(err);
1269
1499
  }
1270
1500
  });
1501
+ agents
1502
+ .command("rotate <agentUid>")
1503
+ .description("Issue a fresh enrollment code for an external agent (old secret + host key stop minting)")
1504
+ .option("--company <slug>", "Company slug (resolves to companyUid)")
1505
+ .option("--json", "Emit raw JSON (code included — pipe carefully)")
1506
+ .action(async function (agentUid, opts) {
1507
+ try {
1508
+ const token = (await resolveVaultCredential()).token;
1509
+ const result = await rotateAgent(token, agentUid);
1510
+ if (opts.json) {
1511
+ console.log(JSON.stringify(result, null, 2));
1512
+ return;
1513
+ }
1514
+ for (const line of formatRotateOutput(result))
1515
+ console.log(line);
1516
+ }
1517
+ catch (err) {
1518
+ fail(err);
1519
+ }
1520
+ });
1521
+ agents
1522
+ .command("revoke <agentUid>")
1523
+ .description("Revoke an external agent: disable its login and drop it from the team")
1524
+ .option("--company <slug>", "Company slug (resolves to companyUid)")
1525
+ .option("--remove", "Also delete the agent entity (it disappears from the roster)")
1526
+ .option("--yes", "Confirm the revoke (required)")
1527
+ .action(async function (agentUid, opts) {
1528
+ if (!opts.yes) {
1529
+ console.error(chalk.yellow(`This will revoke agent ${agentUid}: its credentials stop working immediately and it ` +
1530
+ `leaves the team${opts.remove ? ", and the agent record is deleted" : ""}. ` +
1531
+ `Re-run with --yes to confirm: hq agents revoke ${agentUid}${opts.remove ? " --remove" : ""} --yes`));
1532
+ process.exit(1);
1533
+ }
1534
+ try {
1535
+ const token = (await resolveVaultCredential()).token;
1536
+ const result = await revokeAgent(token, agentUid, opts.remove === true);
1537
+ console.log(chalk.green(result.removed
1538
+ ? `Revoked and removed agent ${agentUid}.`
1539
+ : `Revoked agent ${agentUid}. Its next mint will be refused (401).`));
1540
+ }
1541
+ catch (err) {
1542
+ fail(err);
1543
+ }
1544
+ });
1271
1545
  // Off-box job control (US-003). Nested group so `hq agents jobs --help`
1272
1546
  // lists list|pause|cancel. Same vaultApiFetch + person JWT as the rest of
1273
1547
  // this file; keyed HQ_API_KEY rewrites /v1/agents → /v1/keys/agents.
@@ -1,6 +1,6 @@
1
1
  import { Command } from "commander";
2
2
  import { type RuntimeSignInIssue } from "../lib/bot/runtime-sign-in.js";
3
- import { type BotKind, type BotMemoryMode, type BotRuntimeId, type LaunchctlExec } from "../lib/bot/index.js";
3
+ import { type BotLocalConfig, type MyLocalBot, type BotKind, type BotMemoryMode, type BotNotRunnableReason, type BotConfig, type BotRuntimeId, type LaunchctlExec } from "../lib/bot/index.js";
4
4
  /** Real launchctl; every failure is a non-ok result, never a throw. */
5
5
  export declare const defaultLaunchctl: LaunchctlExec;
6
6
  /** The desktop app creates HQ's setup bot under this name, always from the `setup` worker. */
@@ -101,5 +101,144 @@ export declare function runBotSet(nameArg: string, opts: {
101
101
  effort?: string;
102
102
  json?: boolean;
103
103
  }): Promise<void>;
104
+ /**
105
+ * The one bot the caller means. Matches the local folder name, the cloud slug,
106
+ * the display name, or the agt_ uid; an ambiguous name is refused rather than
107
+ * guessed at.
108
+ */
109
+ export declare function matchRemoteBots(bots: readonly MyLocalBot[], wanted: string): MyLocalBot[];
110
+ export declare function resolveRemoteBot(bots: readonly MyLocalBot[], wanted: string): MyLocalBot;
111
+ /**
112
+ * Why HQ could not say which bots the account owns. The desktop app switches
113
+ * on these values, so they are contract, not prose:
114
+ *
115
+ * server-unsupported the HQ Cloud in front of us has no listing route
116
+ * auth the sign-in was refused
117
+ * network HQ could not be reached at all
118
+ * server-error HQ answered 5xx
119
+ * error anything else
120
+ */
121
+ export type RemoteBotsUnavailableReason = "server-unsupported" | "auth" | "network" | "server-error" | "error";
122
+ export interface RemoteBotsUnavailable {
123
+ reason: RemoteBotsUnavailableReason;
124
+ message: string;
125
+ }
126
+ /**
127
+ * Turn any failure of `GET /v1/agents/mine` into one plain sentence plus a
128
+ * stable reason. Production HQ Cloud does not serve that route yet, and an
129
+ * uncaught 404 reached the person verbatim as "HQ API /v1/agents/mine → 404:
130
+ * Not found" — and, under --json, as no document at all.
131
+ */
132
+ export declare function remoteBotsUnavailable(err: unknown): RemoteBotsUnavailable;
133
+ /**
134
+ * A cloud teardown that only got part of the way, said in the same vocabulary
135
+ * the desktop switches on, plus the one decision that follows from it.
136
+ *
137
+ * `DELETE /v1/agents/{uid}` answers 200 with `terminal:false` and a per-step
138
+ * `failures` map when it could do only part of the job. Those strings are raw
139
+ * infrastructure: one real answer carried the AWS account id, an assumed-role
140
+ * ARN and a DynamoDB table ARN. They are classified here and never repeated —
141
+ * `detail` is a plain clause, `steps` is the server's own step names and
142
+ * nothing of what they said.
143
+ *
144
+ * `retryable` is the local-state decision. Running `hq bot rm` again can only
145
+ * finish the job when the step that failed might behave differently next time.
146
+ * A refusal, or a step whose target was already gone, answers the same way
147
+ * forever, so the local copy goes and the message says the leftover is HQ's.
148
+ *
149
+ * `reason` reuses {@link RemoteBotsUnavailableReason}: `auth` for a refused
150
+ * step (a permission problem, on either side of the call), `error` for a step
151
+ * whose target was already gone, `server-error` for anything HQ may yet
152
+ * finish.
153
+ */
154
+ export interface BotRemovalIncomplete {
155
+ reason: RemoteBotsUnavailableReason;
156
+ /** One plain clause naming what stopped. Never a transport string. */
157
+ detail: string;
158
+ /** Whether running the same command again can still finish the removal. */
159
+ retryable: boolean;
160
+ /** The server's step names only — no ids, ARNs or messages. */
161
+ steps: string[];
162
+ }
163
+ export declare function botRemovalIncomplete(failures?: Record<string, string>): BotRemovalIncomplete;
164
+ /**
165
+ * Rebuild a bot.json from the settings the cloud kept for it. A bot created
166
+ * before HQ saved settings comes back on defaults, and anything this HQ can no
167
+ * longer provide (a worker that is not here, a thinking level the chosen
168
+ * runtime does not accept) is reported rather than silently kept.
169
+ */
170
+ export declare function configFromLocalConfig(input: {
171
+ name: string;
172
+ agentUid: string;
173
+ ownerUid: string;
174
+ hqRoot: string;
175
+ dir: string;
176
+ local: BotLocalConfig | null;
177
+ overrides?: {
178
+ runtime?: BotRuntimeId;
179
+ model?: string;
180
+ };
181
+ now?: () => Date;
182
+ }): {
183
+ config: BotConfig;
184
+ notes: string[];
185
+ };
186
+ export interface BotAdoptResult {
187
+ name: string;
188
+ agentUid: string;
189
+ runtime: BotRuntimeId;
190
+ kind: BotKind;
191
+ companies: string[];
192
+ dir: string;
193
+ worker: string;
194
+ memory: BotMemoryMode;
195
+ memoryDir: string;
196
+ /** True when the cloud had no saved settings and defaults were used. */
197
+ defaulted: boolean;
198
+ notes: string[];
199
+ daemon: string | null;
200
+ }
201
+ export declare function runBotAdopt(nameArg: string, opts: {
202
+ runtime?: string;
203
+ model?: string;
204
+ daemon?: boolean;
205
+ start?: boolean;
206
+ json?: boolean;
207
+ }): Promise<void>;
208
+ export interface BotRestoreRow {
209
+ name: string;
210
+ agentUid: string;
211
+ /** restored = brought back here; repaired = new credentials for one already here. */
212
+ action: "restored" | "repaired" | "skipped" | "failed" | "would-restore" | "would-repair" | "would-skip";
213
+ detail: string;
214
+ /** Present only on a bot skipped because this computer cannot run it. */
215
+ reason?: BotNotRunnableReason;
216
+ }
217
+ /**
218
+ * `hq bot restore` — every local bot this account owns that is not set up on
219
+ * this computer comes back. This is what the desktop app and a post-install
220
+ * hook call after a reinstall or on a new Mac.
221
+ */
222
+ export declare function runBotRestore(opts: {
223
+ all?: boolean;
224
+ dryRun?: boolean;
225
+ daemon?: boolean;
226
+ start?: boolean;
227
+ json?: boolean;
228
+ }): Promise<void>;
229
+ export interface RemoteBotRow {
230
+ name: string;
231
+ agentUid: string;
232
+ kind: string;
233
+ online: boolean;
234
+ lastHeartbeatAt: string | null;
235
+ /** True when this bot is set up on this computer. */
236
+ here: boolean;
237
+ settings: string;
238
+ /** False when this computer cannot run it, whatever the listing says. */
239
+ runnable: boolean;
240
+ /** Why not, when `runnable` is false: the same vocabulary adopt and restore use. */
241
+ reason: BotNotRunnableReason | null;
242
+ }
104
243
  export declare function registerBotCommand(program: Command): void;
105
244
  //# sourceMappingURL=bot.d.ts.map