@indigoai-us/hq-cli 5.345.191 → 5.345.194

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 (129) hide show
  1. package/CHANGELOG.md +431 -0
  2. package/assets/bot-workers/setup/worker.yaml +91 -34
  3. package/dist/command-catalog.generated.d.ts +145 -19
  4. package/dist/command-catalog.generated.js +184 -21
  5. package/dist/commands/bot-jobs.d.ts +21 -18
  6. package/dist/commands/bot-jobs.js +127 -39
  7. package/dist/commands/bot.d.ts +4 -3
  8. package/dist/commands/bot.js +265 -140
  9. package/dist/commands/core-jobs-schedule.js +10 -0
  10. package/dist/commands/daemon.js +37 -0
  11. package/dist/commands/files.d.ts +41 -6
  12. package/dist/commands/files.js +108 -27
  13. package/dist/commands/pack-start-bot.d.ts +4 -6
  14. package/dist/commands/pack-start-bot.js +4 -8
  15. package/dist/commands/signals-run.d.ts +46 -0
  16. package/dist/commands/signals-run.js +180 -0
  17. package/dist/commands/signals.d.ts +1 -0
  18. package/dist/commands/signals.js +3 -0
  19. package/dist/commands/sources.d.ts +58 -0
  20. package/dist/commands/sources.js +294 -26
  21. package/dist/commands/staff/reset-test-account.d.ts +10 -0
  22. package/dist/commands/staff/reset-test-account.js +18 -2
  23. package/dist/commands/whoami.d.ts +7 -0
  24. package/dist/commands/whoami.js +35 -2
  25. package/dist/hq-cloud-sync-runner/bin/sync-runner.js +31171 -52512
  26. package/dist/hq-cloud-sync-runner/package.json +1 -1
  27. package/dist/lib/bot/api.d.ts +3 -2
  28. package/dist/lib/bot/config.d.ts +2 -0
  29. package/dist/lib/bot/fleet-cron.d.ts +121 -0
  30. package/dist/lib/bot/fleet-cron.js +319 -0
  31. package/dist/lib/bot/fleet-local.d.ts +93 -1
  32. package/dist/lib/bot/fleet-local.js +546 -34
  33. package/dist/lib/bot/fleet-owner-companies.d.ts +12 -0
  34. package/dist/lib/bot/fleet-owner-companies.js +30 -0
  35. package/dist/lib/bot/fleet-watch.d.ts +25 -0
  36. package/dist/lib/bot/fleet-watch.js +51 -0
  37. package/dist/lib/bot/index.d.ts +1 -1
  38. package/dist/lib/bot/index.js +1 -1
  39. package/dist/lib/bot/job-run.d.ts +5 -4
  40. package/dist/lib/bot/job-run.js +5 -4
  41. package/dist/lib/bot/jobs/hot-signal-sync.d.ts +57 -3
  42. package/dist/lib/bot/jobs/hot-signal-sync.js +163 -4
  43. package/dist/lib/bot/local-jobs.d.ts +13 -2
  44. package/dist/lib/bot/local-jobs.js +17 -4
  45. package/dist/lib/bot/progress.d.ts +14 -0
  46. package/dist/lib/bot/progress.js +47 -1
  47. package/dist/lib/bot/run.d.ts +55 -2
  48. package/dist/lib/bot/run.js +192 -10
  49. package/dist/lib/bot/setup-tool-offer.d.ts +1 -0
  50. package/dist/lib/bot/setup-tool-offer.js +16 -0
  51. package/dist/lib/bot/signals-engine-flag.d.ts +18 -0
  52. package/dist/lib/bot/signals-engine-flag.js +30 -0
  53. package/dist/lib/daemon/catalog.d.ts +4 -0
  54. package/dist/lib/daemon/catalog.js +29 -9
  55. package/dist/lib/daemon/legacy.d.ts +6 -0
  56. package/dist/lib/daemon/legacy.js +16 -10
  57. package/dist/lib/daemon/run.d.ts +1 -0
  58. package/dist/lib/daemon/run.js +10 -0
  59. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  60. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  61. package/dist/lib/flag-registry.d.ts +13 -1
  62. package/dist/lib/flag-registry.js +46 -0
  63. package/dist/lib/hot-channels/adapters/attio.d.ts +75 -2
  64. package/dist/lib/hot-channels/adapters/attio.js +367 -29
  65. package/dist/lib/hot-channels/adapters/gmail.d.ts +14 -0
  66. package/dist/lib/hot-channels/adapters/gmail.js +59 -2
  67. package/dist/lib/hot-channels/adapters/linear.d.ts +24 -0
  68. package/dist/lib/hot-channels/adapters/linear.js +71 -1
  69. package/dist/lib/hot-channels/adapters/meetings.js +4 -1
  70. package/dist/lib/hot-channels/adapters/slack.js +7 -3
  71. package/dist/lib/hot-channels/alert.js +1 -1
  72. package/dist/lib/hot-channels/contract.d.ts +11 -0
  73. package/dist/lib/hot-channels/contract.js +1 -0
  74. package/dist/lib/hot-channels/cursors.js +1 -1
  75. package/dist/lib/hot-channels/landing.d.ts +6 -2
  76. package/dist/lib/hot-channels/landing.js +13 -6
  77. package/dist/lib/hot-channels/ledger.js +2 -0
  78. package/dist/lib/hot-channels/loop.d.ts +6 -0
  79. package/dist/lib/hot-channels/loop.js +19 -4
  80. package/dist/lib/hot-channels/registry.js +9 -1
  81. package/dist/lib/hot-channels/run.d.ts +18 -0
  82. package/dist/lib/hot-channels/run.js +15 -4
  83. package/dist/lib/hot-channels/signals-pass.d.ts +34 -2
  84. package/dist/lib/hot-channels/signals-pass.js +149 -38
  85. package/dist/lib/hot-channels/signals-redo.d.ts +60 -0
  86. package/dist/lib/hot-channels/signals-redo.js +207 -0
  87. package/dist/lib/hot-channels/sync-lock.js +22 -6
  88. package/dist/lib/mesh/live/daemon/install.d.ts +22 -0
  89. package/dist/lib/mesh/live/daemon/install.js +117 -30
  90. package/dist/lib/signals/adapters-catalog.d.ts +77 -0
  91. package/dist/lib/signals/adapters-catalog.js +194 -0
  92. package/dist/lib/signals/engine.d.ts +202 -0
  93. package/dist/lib/signals/engine.js +504 -0
  94. package/dist/lib/signals/failure-alert.d.ts +89 -0
  95. package/dist/lib/signals/failure-alert.js +175 -0
  96. package/dist/lib/signals/local-date.d.ts +3 -0
  97. package/dist/lib/signals/local-date.js +6 -0
  98. package/dist/lib/signals/projection.d.ts +42 -0
  99. package/dist/lib/signals/projection.js +67 -0
  100. package/dist/lib/signals/reply.d.ts +112 -0
  101. package/dist/lib/signals/reply.js +368 -0
  102. package/dist/lib/signals/run-lock.d.ts +36 -0
  103. package/dist/lib/signals/run-lock.js +148 -0
  104. package/dist/lib/signals/run-record.d.ts +24 -0
  105. package/dist/lib/signals/run-record.js +82 -0
  106. package/dist/lib/signals/sanitize.d.ts +105 -0
  107. package/dist/lib/signals/sanitize.js +439 -0
  108. package/dist/lib/signals/settings.d.ts +10 -0
  109. package/dist/lib/signals/settings.js +48 -0
  110. package/dist/lib/signals/steps/adapters.d.ts +14 -0
  111. package/dist/lib/signals/steps/adapters.js +156 -0
  112. package/dist/lib/signals/steps/entity-similarity.d.ts +10 -0
  113. package/dist/lib/signals/steps/entity-similarity.js +47 -0
  114. package/dist/lib/signals/steps/extract.d.ts +118 -0
  115. package/dist/lib/signals/steps/extract.js +793 -0
  116. package/dist/lib/signals/steps/garden.d.ts +24 -0
  117. package/dist/lib/signals/steps/garden.js +250 -0
  118. package/dist/lib/signals/steps/types.d.ts +117 -0
  119. package/dist/lib/signals/steps/types.js +4 -0
  120. package/dist/lib/signals/steps/upload.d.ts +43 -0
  121. package/dist/lib/signals/steps/upload.js +324 -0
  122. package/dist/lib/signals/write-guard.d.ts +63 -0
  123. package/dist/lib/signals/write-guard.js +287 -0
  124. package/dist/utils/resident-package-preload.js +16 -0
  125. package/dist/utils/vault-api.d.ts +1 -0
  126. package/dist/utils/vault-api.js +1 -1
  127. package/package.json +9 -9
  128. package/dist/lib/bot/fleet-default-flag.d.ts +0 -31
  129. package/dist/lib/bot/fleet-default-flag.js +0 -127
@@ -1,4 +1,7 @@
1
1
  import { withLocalJobs } from "../lib/bot/local-jobs.js";
2
+ import { runSignals } from "../lib/signals/engine.js";
3
+ import { BOT_SIGNALS_ENGINE_JOB_FLAG, signalsEngineForJob } from "../lib/bot/signals-engine-flag.js";
4
+ import { resolveProcessFlagGate } from "../lib/flag-registry.js";
2
5
  import { currentHotSignalSyncCompany, readActiveCompanySlug, registerHotSignalSyncOnStart, runHotSignalSyncJob } from "../lib/bot/jobs/hot-signal-sync.js";
3
6
  import { createClaudeSignalsPass } from "../lib/hot-channels/signals-pass.js";
4
7
  import { machineTimeZone } from "../lib/bot/job-run.js";
@@ -10,7 +13,7 @@ import { hasPromotionHold, promotionHoldSummary } from "../lib/bot/promotion-hol
10
13
  /**
11
14
  * `hq bot` — personal local bots (local-bots US-003/US-004/US-005).
12
15
  *
13
- * hq bot create <name> [--kind personal|company] [--company slug]... [--runtime claude|codex|grok] [--model m] [--effort level] [--worker id] [--intro text] [--kickoff prompt] [--memory synced|local] [--no-daemon]
16
+ * hq bot create <name> [--kind personal|company] [--company slug]... [--runtime claude|codex|grok] [--model m] [--worker id] [--intro text] [--kickoff prompt] [--memory synced|local] [--no-daemon]
14
17
  * hq bot companies <name> [--add slug]... [--remove slug]... (a company bot's memberships)
15
18
  * hq bot workers [--json] (company + core workers a bot can be created from)
16
19
  * hq bot list [--remote] [--json]
@@ -39,6 +42,12 @@ import { createVaultObjectDownloader } from "../lib/agent-kit/attachment-downloa
39
42
  import { addBotToCompanies, describeCompanyFailures, removeBotFromCompanies } from "./bot-companies.js";
40
43
  import { registerBotContinuityCommand } from "./bot-continuity.js";
41
44
  import { registerBotJobsCommand } from "./bot-jobs.js";
45
+ import { ensureFleetHotSignalSync, fleetCronOps, migrateLegacyJobsToFleet } from "../lib/bot/fleet-cron.js";
46
+ import { resolvePackSetupPrompt } from "../lib/bot/pack-setup.js";
47
+ import { detectCodingToolActivity } from "../lib/bot/coding-tool-activity.js";
48
+ import { setupToolOfferPendingSection, setupToolOfferText, setupWelcomeOpening } from "../lib/bot/setup-tool-offer.js";
49
+ import { patchSetupIntroState, readSetupIntroState, readSetupProgressIntro } from "../lib/bot/setup-explain.js";
50
+ import { resolveHqBinary, resolveNodeBinary } from "../lib/mesh/live/daemon/install.js";
42
51
  import chalk from "chalk";
43
52
  import { execFileSync, spawn } from "node:child_process";
44
53
  import * as fs from "node:fs";
@@ -48,7 +57,6 @@ import * as readline from "node:readline";
48
57
  import { DEFAULT_COGNITO, ensureCognitoToken, resolveDefaultHqRoot, } from "../utils/cognito-session.js";
49
58
  import { peekHqApiKey } from "../utils/resolve-vault-credential.js";
50
59
  import { getCompanyUid, resolveCallerPersonUid } from "../utils/vault-api.js";
51
- import { resolveHqBinary } from "../lib/mesh/live/daemon/install.js";
52
60
  import { askDaemonToStart, askDaemonToStop, daemonOwnsProcess, daemonHoldReason, daemonSupervises, daemonWillSupervise, handOffInstallHere, } from "../lib/daemon/handoff.js";
53
61
  import { daemonPaths } from "../lib/daemon/paths.js";
54
62
  import { launchdLabel } from "../lib/bot/paths.js";
@@ -57,7 +65,7 @@ import { runtimeDisplayName, signInCommand } from "../lib/bot/runtime-sign-in.js
57
65
  import { classifyRuntimeFailure, runtimeFailureReason } from "../lib/bot/runtime-failure.js";
58
66
  import { PROBE_DEFAULT_TIMEOUT_MS, probeBotRuntime } from "../lib/bot/probe.js";
59
67
  import { BOT_LOGIN_RUNTIMES, botAuthEnv, botRuntimeAuth, capacityAccountHome, describeBotAuth, formatBotAuth, isBotLoginRuntime, restoreBotSessions, setAsideBotSessions, signInBotRuntime, signOutBotRuntime, withRuntimeAuth, } from "../lib/bot/runtime-auth.js";
60
- import { BotApi, BotApiError, BOT_RESTORE_DEFAULTS, buildBotLocalConfig, describeBotLocalConfig, orphanBotRunMessage, parseBotLocalConfig, removeOrphanBotDaemon, botDaemonStatus, botDir, botCredsPath, botLogPath, botsRoot, botTokenStateDir, botWorkerRelDir, buildBotDaemonPaths, createBotLogger, deleteBotCreds, installBotDaemon, repairBotDaemonIfStale, healBotDaemonNodeFromJob, introDmText, isBotMemoryMode, botLocalMemoryDir, botMemoryMode, resolveBotMemoryDir, validateBotIntro, validateBotDisplayName, validateBotKickoff, validateBotEffort, validateBotModel, effectiveBotEffort, BOT_EFFORT_LEVELS, DEFAULT_BOT_EFFORT, BOT_MEMORY_MODES, BOT_KINDS, isBotKind, effectiveBotKind, effectiveBotCompanies, normalizeBotCompanies, SETUP_BOT_WORKER_ID, isBotRuntimeId, localNameForRemoteBot, remoteBotRunnability, isPidAlive, listBotWorkerOptions, patchBotConfig, readBotConfig, readBotCredsIdentity, readBotStatus, runtimeCrashSinceStart, resolveBotWorker, runBot, runtimeFor, scaffoldBotMemory, scaffoldBotWorker, startBotDaemon, stopBotDaemon, uninstallBotDaemon, validateBotName, writeBotConfig, writeBotCreds, BOT_RUNTIMES, BOT_INTRO_MAX_CHARS, BOT_DISPLAY_NAME_MAX_CHARS, BOT_KICKOFF_MAX_CHARS, detectBotPlatform, withBotLock, localBotRole, remoteBotRole, remoteBotDisplayName, personFacingName, writeCloudDisplayName, setupSlugFor, ASSISTANT_ROLE, HQ_FLEET_RUNTIME_ENGINE_ID, authenticateFleetLocalBot, bootstrapFleetLocalBot, buildSystemPrompt, controlFleetLocalBot, ensureFleetPython, fleetLocalGatewayPid, fleetLocalProfileNeedsAdoption, fleetLocalServiceInstalled, fleetProviderForRuntime, importFleetLocalMachineCredential, fleetProviderNeedsSignIn, fleetDefaultCompanies, fleetOwnerCompanies, fleetRuntimeIsDefault, refreshFleetLocalInstructions, fleetLocalPromotionRuntime, installPinnedFleetLocalDistribution, readPromptSources, removeFleetLocalBot, } from "../lib/bot/index.js";
68
+ import { BotApi, BotApiError, BOT_RESTORE_DEFAULTS, buildBotLocalConfig, describeBotLocalConfig, orphanBotRunMessage, parseBotLocalConfig, removeOrphanBotDaemon, botDaemonStatus, botDir, botCredsPath, botLogPath, botsRoot, botTokenStateDir, botWorkerRelDir, buildBotDaemonPaths, createBotLogger, deleteBotCreds, installBotDaemon, repairBotDaemonIfStale, healBotDaemonNodeFromJob, introDmText, isBotMemoryMode, botLocalMemoryDir, botMemoryMode, resolveBotMemoryDir, validateBotIntro, validateBotDisplayName, validateBotKickoff, validateBotEffort, validateBotModel, effectiveBotEffort, BOT_EFFORT_LEVELS, DEFAULT_BOT_EFFORT, BOT_MEMORY_MODES, BOT_KINDS, isBotKind, effectiveBotKind, effectiveBotCompanies, normalizeBotCompanies, SETUP_BOT_WORKER_ID, isBotRuntimeId, localNameForRemoteBot, remoteBotRunnability, isPidAlive, listBotWorkerOptions, patchBotConfig, readBotConfig, readBotCredsIdentity, readBotStatus, runtimeCrashSinceStart, resolveBotWorker, runBot, runtimeFor, scaffoldBotMemory, scaffoldBotWorker, startBotDaemon, stopBotDaemon, uninstallBotDaemon, validateBotName, writeBotConfig, writeBotCreds, BOT_RUNTIMES, BOT_INTRO_MAX_CHARS, BOT_DISPLAY_NAME_MAX_CHARS, BOT_KICKOFF_MAX_CHARS, detectBotPlatform, withBotLock, localBotRole, remoteBotRole, remoteBotDisplayName, personFacingName, writeCloudDisplayName, setupSlugFor, ASSISTANT_ROLE, HQ_FLEET_RUNTIME_ENGINE_ID, HQ_FLEET_LOCAL_RELEASE, authenticateFleetLocalBot, bootstrapFleetLocalBot, buildSystemPrompt, controlFleetLocalBot, ensureFleetPython, fleetLocalGatewayPid, fleetLocalProfileNeedsAdoption, fleetLocalServiceInstalled, fleetProviderForRuntime, importFleetLocalMachineCredential, fleetProviderNeedsSignIn, fleetOwnerCompanies, refreshFleetLocalInstructions, fleetLocalPromotionRuntime, installPinnedFleetLocalDistribution, installTargetFleetLocalDistribution, resolveFleetLocalPointerTarget, resolveFleetLocalTargetRelease, fleetLocalProfileDir, fleetProfileOwnerCompanies, fleetLocalChannels, readFleetLocalGatewayState, readPromptSources, removeFleetLocalBot, } from "../lib/bot/index.js";
61
69
  function collectRepeated(value, previous) {
62
70
  return [...previous, value];
63
71
  }
@@ -128,7 +136,15 @@ function isFleetRuntimeBot(config) {
128
136
  * runtime's own way of labelling senders.
129
137
  */
130
138
  function fleetBotSystemPrompt(name, config, overrides = {}) {
131
- return buildSystemPrompt({
139
+ let packSetup;
140
+ try {
141
+ packSetup = resolvePackSetupPrompt({ hqRoot: config.hqRoot, workerDir: config.workerDir, workerSource: config.workerSource });
142
+ }
143
+ catch {
144
+ packSetup = null;
145
+ }
146
+ const prompt = buildSystemPrompt({
147
+ packSetup,
132
148
  botName: name,
133
149
  ...(config.displayName ? { displayName: config.displayName } : {}),
134
150
  agentUid: config.agentUid,
@@ -148,6 +164,119 @@ function fleetBotSystemPrompt(name, config, overrides = {}) {
148
164
  companySlug: config.companySlug,
149
165
  }),
150
166
  });
167
+ const offer = pendingFleetSetupToolOffer(name, config);
168
+ return offer ? `${prompt.trimEnd()}\n\n${offer}` : prompt;
169
+ }
170
+ function isFleetSetupBot(name, config) {
171
+ return name === RESERVED_SETUP_BOT_NAME && effectiveBotKind(config) === "personal" && config.workerId === SETUP_BOT_WORKER_ID;
172
+ }
173
+ function pendingFleetSetupToolOffer(name, config) {
174
+ const kickoff = (config.kickoff ?? "").trim();
175
+ if (!kickoff || !isFleetSetupBot(name, config))
176
+ return null;
177
+ try {
178
+ const state = readSetupIntroState(botDir(name));
179
+ if (!state.kickoffDeferred || !state.toolOffer)
180
+ return null;
181
+ if (readSetupProgressIntro(resolveBotMemoryDir(config)) !== null)
182
+ return null;
183
+ return setupToolOfferPendingSection(state.toolOffer.tool, kickoff);
184
+ }
185
+ catch {
186
+ return null;
187
+ }
188
+ }
189
+ function fleetSetupToolOffer(dir, config) {
190
+ try {
191
+ if (readSetupIntroState(dir).toolOffer)
192
+ return null;
193
+ if (readSetupProgressIntro(resolveBotMemoryDir(config)) !== null)
194
+ return null;
195
+ const tool = detectCodingToolActivity({ now: new Date(), extraHqRoots: [config.hqRoot] }).pick;
196
+ if (!tool)
197
+ return null;
198
+ patchSetupIntroState(dir, { toolOffer: { tool, offeredAt: new Date().toISOString() }, kickoffDeferred: true });
199
+ createBotLogger(dir)("info", `offered to continue setup in the ${tool} app; the kickoff waits for the answer`);
200
+ return tool;
201
+ }
202
+ catch (err) {
203
+ createBotLogger(dir)("warn", `coding tool check failed (${err instanceof Error ? err.message : String(err)}); running the kickoff`);
204
+ return null;
205
+ }
206
+ }
207
+ /** The daily Fleet signals job is gated by the same default-off flag as the in-process engine job. */
208
+ function fleetSignalsJobEnabled() {
209
+ try {
210
+ return resolveProcessFlagGate(BOT_SIGNALS_ENGINE_JOB_FLAG, { fallback: false }, () => false);
211
+ }
212
+ catch {
213
+ return false;
214
+ }
215
+ }
216
+ function syncFleetRuntimeJobs(name, dir, config) {
217
+ const log = createBotLogger(dir);
218
+ let ops;
219
+ try {
220
+ ops = fleetCronOps(config.agentUid);
221
+ }
222
+ catch (err) {
223
+ log("warn", `could not reach ${name}'s HQ Fleet Runtime schedule: ${err instanceof Error ? err.message : String(err)}`);
224
+ return;
225
+ }
226
+ try {
227
+ const migrated = migrateLegacyJobsToFleet(dir, config, ops);
228
+ if (migrated) {
229
+ const movedCount = migrated.imported;
230
+ const moved = `moved ${movedCount} scheduled job${movedCount === 1 ? "" : "s"} into the HQ Fleet Runtime${migrated.alreadyPresent ? ` (${migrated.alreadyPresent} already there)` : ""}`;
231
+ if (migrated.skipped.length === 0) {
232
+ log("info", `${moved}; the old list is kept at ${migrated.renamedTo}`);
233
+ }
234
+ else {
235
+ const message = `${moved}, but could not move ${migrated.skipped.length} scheduled job(s) (${migrated.skipped.join(", ")}): the HQ Fleet Runtime cannot run their schedule. They are still in ${path.join(dir, "jobs.json")}, which was not renamed, and they will not run until you add them again with hq bot jobs --add using a schedule the runtime supports.`;
236
+ log("warn", message);
237
+ console.warn(`Warning: ${name}: ${message}`);
238
+ }
239
+ }
240
+ }
241
+ catch (err) {
242
+ log("warn", `could not move ${name}'s scheduled jobs into the HQ Fleet Runtime: ${err instanceof Error ? err.message : String(err)}`);
243
+ }
244
+ try {
245
+ let otherBotDirs = [];
246
+ try {
247
+ otherBotDirs = listBotNames(botsRoot()).map((n) => botDir(n));
248
+ }
249
+ catch {
250
+ otherBotDirs = [];
251
+ }
252
+ const result = ensureFleetHotSignalSync({
253
+ dir,
254
+ config,
255
+ ops,
256
+ profileDir: fleetLocalProfileDir(config.agentUid),
257
+ activeCompany: readActiveCompanySlug(config.hqRoot) ?? null,
258
+ ownerCompanies: fleetProfileOwnerCompanies(config.agentUid),
259
+ otherBotDirs,
260
+ nodeBinary: resolveNodeBinary(),
261
+ hqBinary: resolveHqBinary(),
262
+ signalsJobEnabled: fleetSignalsJobEnabled(),
263
+ });
264
+ for (const id of result.removed)
265
+ log("info", `removed the hot-signal-sync job ${id}: this bot no longer syncs that company`);
266
+ for (const entry of result.companies) {
267
+ if (entry.created)
268
+ log("info", `registered the daily hot-signal-sync job for ${entry.company} at 06:30 in the HQ Fleet Runtime`);
269
+ if (entry.heldBy)
270
+ log("info", `not adding a hot-signal-sync job for ${entry.company}: the bot in ${entry.heldBy} already runs it`);
271
+ }
272
+ }
273
+ catch (err) {
274
+ log("warn", `could not set up ${name}'s hot-signal-sync job: ${err instanceof Error ? err.message : String(err)}`);
275
+ }
276
+ }
277
+ function refuseHeldFleetBot(dir) {
278
+ if (hasPromotionHold(dir))
279
+ throw new Error("This bot is held for cloud promotion. Continue its promotion before changing local settings or lifecycle.");
151
280
  }
152
281
  function fleetAuthCommand(name, config) {
153
282
  return `hq bot auth ${name} --provider ${config.runtime === "grok" ? "grok" : config.runtime === "codex" ? "chatgpt" : "claude"}`;
@@ -179,6 +308,7 @@ function runFleetRuntimeControl(name, config, action, json) {
179
308
  }
180
309
  if (action === "install" || action === "start" || action === "restart") {
181
310
  refreshFleetLocalInstructions(config.agentUid, fleetBotSystemPrompt(name, config));
311
+ syncFleetRuntimeJobs(name, botDir(name), config);
182
312
  }
183
313
  const installService = action === "install" || (config.fleetServiceInstallPending === true && (action === "start" || action === "restart"));
184
314
  const result = controlFleetLocalBot(config.agentUid, installService ? "install" : action);
@@ -453,6 +583,7 @@ async function startFleetRuntimeBot(name, dir, config, output, extra) {
453
583
  }
454
584
  const adopted = await adoptFleetRuntimeProfile(name, dir, config);
455
585
  refreshFleetLocalInstructions(config.agentUid, fleetBotSystemPrompt(name, config));
586
+ syncFleetRuntimeJobs(name, dir, config);
456
587
  const before = readBotStatus(dir);
457
588
  const alreadyRunning = !!before && before.state === "running" && isPidAlive(before.pid);
458
589
  const installService = adopted || config.fleetServiceInstallPending === true;
@@ -477,10 +608,28 @@ async function startFleetRuntimeBot(name, dir, config, output, extra) {
477
608
  console.log(chalk.yellow(`${name} did not report running within 45s`) + chalk.dim(` logs: ~/.hq-fleet/profiles/${config.agentUid}/logs`));
478
609
  return { running, how };
479
610
  }
480
- async function adoptFleetRuntimeProfile(name, dir, config) {
481
- if (!fleetLocalProfileNeedsAdoption(config.agentUid))
611
+ async function adoptFleetRuntimeProfile(name, dir, config, opts = {}) {
612
+ const target = opts.pointerOnly ? await resolveFleetLocalPointerTarget() : await resolveFleetLocalTargetRelease();
613
+ if (!target)
614
+ return false;
615
+ if (!fleetLocalProfileNeedsAdoption(config.agentUid, { release: target.release }))
482
616
  return false;
483
- const installation = await installPinnedFleetLocalDistribution();
617
+ let installation;
618
+ try {
619
+ installation = await installTargetFleetLocalDistribution({ target });
620
+ }
621
+ catch (err) {
622
+ if (target.source !== "pointer")
623
+ throw err;
624
+ if (opts.pointerOnly) {
625
+ createBotLogger(dir)("warn", `could not install HQ Fleet Local ${target.release.version} (${err instanceof Error ? err.message : String(err)}); the bot stays on its current release`);
626
+ return false;
627
+ }
628
+ createBotLogger(dir)("warn", `could not install HQ Fleet Local ${target.release.version} (${err instanceof Error ? err.message : String(err)}); staying on ${HQ_FLEET_LOCAL_RELEASE.version}`);
629
+ if (!fleetLocalProfileNeedsAdoption(config.agentUid, { release: HQ_FLEET_LOCAL_RELEASE }))
630
+ return false;
631
+ installation = await installPinnedFleetLocalDistribution();
632
+ }
484
633
  const python = await ensureFleetPython();
485
634
  const companyScope = effectiveBotKind(config) === "company"
486
635
  ? effectiveBotCompanies(config)
@@ -494,7 +643,7 @@ async function adoptFleetRuntimeProfile(name, dir, config) {
494
643
  systemPrompt: fleetBotSystemPrompt(name, config),
495
644
  provider: fleetProviderForRuntime(config.runtime, config.model),
496
645
  ownerCompanies: companyScope,
497
- ...(config.autoApprove === false ? { autoApprove: false } : {}),
646
+ ...(config.autoApprove === true ? { autoApprove: true } : {}),
498
647
  }, { python, start: false });
499
648
  patchBotConfig(dir, { engine: installation.engine });
500
649
  const credentials = botCredsPath(dir);
@@ -655,28 +804,6 @@ export function resolveCreateKind(opts, worker) {
655
804
  }
656
805
  return { kind, companies };
657
806
  }
658
- async function promptFleetProvider() {
659
- if (process.stdin.isTTY !== true || process.stderr.isTTY !== true) {
660
- throw Object.assign(new Error("--provider is required with --hq-fleet-runtime in a non-interactive command (claude, codex, or grok)."), { expected: true });
661
- }
662
- const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
663
- try {
664
- const answer = await new Promise((resolve) => {
665
- rl.question("Provider: 1) Codex 2) Claude 3) Grok [1]: ", resolve);
666
- });
667
- const selected = answer.trim().toLowerCase();
668
- if (selected === "" || selected === "1" || selected === "codex")
669
- return "codex";
670
- if (selected === "2" || selected === "claude")
671
- return "claude";
672
- if (selected === "3" || selected === "grok")
673
- return "grok";
674
- throw Object.assign(new Error("Provider must be claude, codex, or grok."), { expected: true });
675
- }
676
- finally {
677
- rl.close();
678
- }
679
- }
680
807
  /**
681
808
  * `hq bot create`. Runs under the bot's lock, so a create racing an adopt,
682
809
  * restore, rm or set of the same bot waits for it instead of tripping over
@@ -807,12 +934,8 @@ async function finishExistingSetupBot(name, dir, config, displayName, opts) {
807
934
  console.log(chalk.yellow(`${name} did not report running within 15s`) + chalk.dim(` logs: hq bot logs ${name}`));
808
935
  }
809
936
  async function runBotCreateLocked(name, opts) {
810
- const requestedFleetRuntime = opts.hqFleetRuntime === true;
811
- if (requestedFleetRuntime && opts.legacyRuntime) {
812
- throw Object.assign(new Error("Pick one of --hq-fleet-runtime and --legacy-runtime."), { expected: true });
813
- }
814
- if (requestedFleetRuntime && (opts.effort || opts.lean)) {
815
- throw Object.assign(new Error("--hq-fleet-runtime sets its own thinking level. Do not combine it with --effort or --lean."), { expected: true });
937
+ if (opts.legacyRuntime) {
938
+ throw Object.assign(new Error("--legacy-runtime is no longer supported: new bots run on the HQ Fleet Runtime. Drop --legacy-runtime."), { expected: true });
816
939
  }
817
940
  if (opts.provider && !isBotRuntimeId(opts.provider)) {
818
941
  throw Object.assign(new Error(`--provider must be one of ${BOT_RUNTIMES.join(", ")}`), { expected: true });
@@ -820,8 +943,7 @@ async function runBotCreateLocked(name, opts) {
820
943
  if (opts.provider && opts.runtime && opts.provider !== opts.runtime) {
821
944
  throw Object.assign(new Error("--provider and --runtime must name the same provider when both are used."), { expected: true });
822
945
  }
823
- const selectedProvider = opts.provider ?? opts.runtime ?? (requestedFleetRuntime ? await promptFleetProvider() : undefined);
824
- const runtime = selectedProvider ?? "claude";
946
+ const runtime = opts.provider ?? opts.runtime ?? "claude";
825
947
  if (!isBotRuntimeId(runtime)) {
826
948
  throw Object.assign(new Error(`--runtime must be one of ${BOT_RUNTIMES.join(", ")}`), { expected: true });
827
949
  }
@@ -833,7 +955,6 @@ async function runBotCreateLocked(name, opts) {
833
955
  const displayName = opts.displayName !== undefined ? validateBotDisplayName(opts.displayName) : undefined;
834
956
  const kickoff = opts.kickoff !== undefined ? validateBotKickoff(opts.kickoff) : undefined;
835
957
  const model = opts.model !== undefined ? validateBotModel(opts.model) : undefined;
836
- const effort = opts.effort !== undefined ? validateBotEffort(runtime, opts.effort) : undefined;
837
958
  const dir = botDir(name);
838
959
  const existingBeforeRoot = readBotConfig(dir);
839
960
  // Only HQ's setup assistant is idempotent; any other bot keeps failing fast.
@@ -856,24 +977,11 @@ async function runBotCreateLocked(name, opts) {
856
977
  // cost an identity).
857
978
  const worker = opts.worker?.trim() ? resolveBotWorker(hqRoot, opts.worker) : null;
858
979
  const { kind, companies } = resolveCreateKind(opts, worker);
859
- if (requestedFleetRuntime && kind === "company" && companies.length !== 1) {
980
+ if (kind === "company" && companies.length !== 1) {
860
981
  throw Object.assign(new Error("An HQ Fleet company bot must belong to exactly one company."), {
861
982
  expected: true,
862
983
  });
863
984
  }
864
- // Companies with the fleet-default flag get their personal bots on
865
- // the HQ Fleet Runtime; --legacy-runtime keeps the model-CLI runner.
866
- const fleetCandidate = requestedFleetRuntime ||
867
- (!opts.legacyRuntime && kind === "personal" && !opts.effort && !opts.lean);
868
- // Owner turns on the bot may use only these companies: the owner's own,
869
- // and only where the company turned the flag on.
870
- const flaggedCompanies = fleetCandidate && kind === "personal" ? await fleetDefaultCompanies(hqRoot) : [];
871
- const useFleetRuntime = requestedFleetRuntime || (fleetCandidate && fleetRuntimeIsDefault(flaggedCompanies));
872
- const ownerCompanies = useFleetRuntime
873
- ? kind === "personal"
874
- ? await fleetOwnerCompanies()
875
- : companies
876
- : [];
877
985
  const isSetupBot = name === RESERVED_SETUP_BOT_NAME && kind === "personal" && worker?.id === SETUP_BOT_WORKER_ID;
878
986
  if (name === RESERVED_SETUP_BOT_NAME && kind === "personal" && !isSetupBot) {
879
987
  throw Object.assign(new Error(`The name ${RESERVED_SETUP_BOT_NAME} is reserved for HQ's assistant; create it with --worker ${RESERVED_SETUP_BOT_NAME}.`), { expected: true });
@@ -888,12 +996,8 @@ async function runBotCreateLocked(name, opts) {
888
996
  expected: true,
889
997
  });
890
998
  }
891
- const fleetInstallation = useFleetRuntime
892
- ? await installPinnedFleetLocalDistribution()
893
- : null;
894
- const fleetPython = fleetInstallation ? await ensureFleetPython() : null;
895
- const fleetProvider = fleetInstallation ? fleetProviderForRuntime(runtime, model) : null;
896
- const fleetAuthPending = fleetProvider !== null && fleetProviderNeedsSignIn(fleetProvider);
999
+ const fleetProvider = fleetProviderForRuntime(runtime, model);
1000
+ const fleetAuthPending = fleetProviderNeedsSignIn(fleetProvider);
897
1001
  const token = await ownerToken();
898
1002
  const owner = await resolveCallerPersonUid(token, undefined, { command: "hq bot create" });
899
1003
  if (!owner)
@@ -986,6 +1090,9 @@ async function runBotCreateLocked(name, opts) {
986
1090
  // of sending another create that the entity store will reject.
987
1091
  if (isSetupBot && (await adoptOwnedSetupBot(true)))
988
1092
  return;
1093
+ const fleetInstallation = await installTargetFleetLocalDistribution();
1094
+ const fleetPython = await ensureFleetPython();
1095
+ const ownerCompanies = kind === "personal" ? await fleetOwnerCompanies() : companies;
989
1096
  // A company bot's companies are resolved first (a typo must not cost an
990
1097
  // identity) and sent with the create: the server makes the bot a member of
991
1098
  // each and refuses the whole create if the owner cannot add agents there.
@@ -1003,12 +1110,11 @@ async function runBotCreateLocked(name, opts) {
1003
1110
  // instead of creating a new one. The machine secret never travels.
1004
1111
  const localConfig = buildBotLocalConfig({
1005
1112
  runtime,
1006
- ...(fleetInstallation ? { engine: fleetInstallation.engine } : {}),
1113
+ engine: fleetInstallation.engine,
1007
1114
  kind,
1008
1115
  ...(kind === "company" ? { companies } : {}),
1009
1116
  ...(model ? { model } : {}),
1010
- ...(effort ? { effort } : {}),
1011
- ...(opts.autoApprove === false ? { autoApprove: false } : {}),
1117
+ ...(opts.autoApprove === true ? { autoApprove: true } : {}),
1012
1118
  ...(worker
1013
1119
  ? { workerSource: "worker", workerId: worker.id, ...(worker.company ? { companySlug: worker.company } : {}) }
1014
1120
  : { workerSource: "scaffold" }),
@@ -1070,13 +1176,12 @@ async function runBotCreateLocked(name, opts) {
1070
1176
  agentUid,
1071
1177
  ownerUid: created.agent.ownerUid ?? owner,
1072
1178
  runtime,
1073
- ...(fleetInstallation ? { engine: fleetInstallation.engine } : {}),
1179
+ engine: fleetInstallation.engine,
1074
1180
  kind,
1075
1181
  // A company bot's list is filled in below, with the invites that went through.
1076
1182
  ...(kind === "company" ? { companies: [] } : {}),
1077
1183
  ...(model ? { model } : {}),
1078
- ...(effort ? { effort } : {}),
1079
- ...(opts.autoApprove === false ? { autoApprove: false } : {}),
1184
+ ...(opts.autoApprove === true ? { autoApprove: true } : {}),
1080
1185
  hqRoot,
1081
1186
  ...(worker
1082
1187
  ? {
@@ -1098,7 +1203,6 @@ async function runBotCreateLocked(name, opts) {
1098
1203
  ...(displayName ? { displayName } : {}),
1099
1204
  ...(intro ? { intro } : {}),
1100
1205
  ...(kickoff ? { kickoff } : {}),
1101
- ...(opts.lean && runtime === "claude" ? { lean: true } : {}),
1102
1206
  createdAt: new Date().toISOString(),
1103
1207
  enabled: !fleetAuthPending,
1104
1208
  ...(fleetAuthPending ? { fleetAuthPending: true } : {}),
@@ -1128,29 +1232,28 @@ async function runBotCreateLocked(name, opts) {
1128
1232
  const scaffoldOpts = { memoryDir: config.memoryDir };
1129
1233
  const scaffold = worker
1130
1234
  ? scaffoldBotMemory(hqRoot, name, undefined, scaffoldOpts)
1131
- : scaffoldBotWorker(hqRoot, name, agentUid, fleetInstallation ? HQ_FLEET_RUNTIME_ENGINE_ID : runtime, undefined, scaffoldOpts);
1132
- if (fleetInstallation)
1133
- fs.rmSync(path.join(dir, "status.json"), { force: true });
1134
- const fleetBootstrap = fleetInstallation && fleetPython
1135
- ? await bootstrapFleetLocalBot({
1136
- agentUid,
1137
- ownerUid: config.ownerUid,
1138
- hqRoot,
1139
- botDir: dir,
1140
- installation: fleetInstallation,
1141
- machineCredentials: botCredsPath(dir),
1142
- systemPrompt: fleetBotSystemPrompt(name, config, { kind, companies }),
1143
- provider: fleetProvider,
1144
- ownerCompanies,
1145
- ...(opts.autoApprove === false ? { autoApprove: false } : {}),
1146
- firstStart: {
1147
- intro: introDmText(name, runtime, intro, { kind, companies }),
1148
- ...(kickoff ? { kickoff } : {}),
1149
- },
1150
- }, { start: opts.start !== false && !fleetAuthPending, python: fleetPython })
1151
- : null;
1152
- const fleetProfile = fleetBootstrap?.profile ?? null;
1153
- const runtimeLabel = fleetInstallation ? HQ_FLEET_RUNTIME_ENGINE_ID : runtime;
1235
+ : scaffoldBotWorker(hqRoot, name, agentUid, HQ_FLEET_RUNTIME_ENGINE_ID, undefined, scaffoldOpts);
1236
+ fs.rmSync(path.join(dir, "status.json"), { force: true });
1237
+ const introText = introDmText(name, runtime, intro, { kind, companies });
1238
+ const toolOffer = isSetupBot && kickoff ? fleetSetupToolOffer(dir, config) : null;
1239
+ const fleetBootstrap = await bootstrapFleetLocalBot({
1240
+ agentUid,
1241
+ ownerUid: config.ownerUid,
1242
+ hqRoot,
1243
+ botDir: dir,
1244
+ installation: fleetInstallation,
1245
+ machineCredentials: botCredsPath(dir),
1246
+ systemPrompt: fleetBotSystemPrompt(name, config, { kind, companies }),
1247
+ provider: fleetProvider,
1248
+ ownerCompanies,
1249
+ ...(opts.autoApprove === true ? { autoApprove: true } : {}),
1250
+ firstStart: toolOffer
1251
+ ? { intro: setupToolOfferText(toolOffer, setupWelcomeOpening(intro)) }
1252
+ : { intro: introText, ...(kickoff ? { kickoff } : {}) },
1253
+ }, { start: opts.start !== false && !fleetAuthPending, python: fleetPython });
1254
+ syncFleetRuntimeJobs(name, dir, config);
1255
+ const fleetProfile = fleetBootstrap.profile;
1256
+ const runtimeLabel = HQ_FLEET_RUNTIME_ENGINE_ID;
1154
1257
  createBotLogger(dir)("info", `created ${name} (${agentUid}) kind=${kind}${companies.length ? ` companies=${companies.join(",")}` : ""} runtime=${runtimeLabel}${worker ? ` worker=${worker.id} company=${worker.company ?? "-"}` : ""} memory=${memoryMode}`);
1155
1258
  const created_ = {
1156
1259
  outcome: "created",
@@ -1161,7 +1264,7 @@ async function runBotCreateLocked(name, opts) {
1161
1264
  kind,
1162
1265
  companies,
1163
1266
  runtime: runtimeLabel,
1164
- engine: fleetInstallation?.engine ?? null,
1267
+ engine: fleetInstallation.engine,
1165
1268
  profile: fleetProfile,
1166
1269
  dir,
1167
1270
  worker: worker ? path.join(hqRoot, worker.path) : scaffold.dir,
@@ -1173,14 +1276,11 @@ async function runBotCreateLocked(name, opts) {
1173
1276
  intro: intro ?? null,
1174
1277
  kickoff: kickoff ?? null,
1175
1278
  model: model ?? null,
1176
- effort: fleetInstallation ? null : effort ?? DEFAULT_BOT_EFFORT,
1177
- ...(fleetProvider ? { provider: fleetProvider.id, needsSignIn: fleetAuthPending, ...(fleetAuthPending ? { authCommand: fleetAuthCommand(name, config) } : {}) } : {}),
1279
+ effort: null,
1280
+ provider: fleetProvider.id,
1281
+ needsSignIn: fleetAuthPending,
1282
+ ...(fleetAuthPending ? { authCommand: fleetAuthCommand(name, config) } : {}),
1178
1283
  };
1179
- let daemonMessage = null;
1180
- if (!fleetInstallation && opts.daemon !== false && process.platform === "darwin") {
1181
- const r = installBotAgent(buildBotDaemonPaths({ name, botDir: dir, hqRoot }));
1182
- daemonMessage = r.message;
1183
- }
1184
1284
  if (!opts.json) {
1185
1285
  console.log(chalk.green(`Created bot ${chalk.bold(name)}`) + chalk.dim(` (${agentUid}, ${runtimeLabel})`));
1186
1286
  console.log(chalk.dim(` kind: ${describeKind(kind, companies)}`));
@@ -1190,38 +1290,24 @@ async function runBotCreateLocked(name, opts) {
1190
1290
  console.log(chalk.dim(` worker: ${scaffold.dir}`));
1191
1291
  console.log(chalk.dim(` memory: ${scaffold.memoryDir} (${memoryMode})`));
1192
1292
  console.log(chalk.dim(` state: ${dir}`));
1193
- if (fleetProfile)
1194
- console.log(chalk.dim(` profile: ${fleetProfile}`));
1195
- if (fleetProvider && fleetProviderNeedsSignIn(fleetProvider)) {
1293
+ console.log(chalk.dim(` profile: ${fleetProfile}`));
1294
+ if (fleetAuthPending) {
1196
1295
  console.log(chalk.yellow(` sign it in before it can answer: hq bot auth ${name} --provider ${runtime === "grok" ? "grok" : "chatgpt"}`));
1197
1296
  }
1198
- if (daemonMessage)
1199
- console.log(chalk.dim(` ${daemonMessage}`));
1200
- }
1201
- if (fleetInstallation) {
1202
- if (opts.json) {
1203
- console.log(JSON.stringify({ ok: true, name, ...created_, daemon: null, running: fleetBootstrap?.running ?? false }, null, 2));
1204
- }
1205
- return;
1206
- }
1207
- if (opts.start !== false) {
1208
- if (daemonMessage && /loaded\)/.test(daemonMessage)) {
1209
- // RunAtLoad already started it.
1210
- const running = await waitFor(() => readBotStatus(dir)?.state === "running", 20_000);
1211
- if (opts.json) {
1212
- console.log(JSON.stringify({ ok: true, name, ...created_, daemon: daemonMessage, running }, null, 2));
1213
- }
1214
- else if (running)
1215
- console.log(chalk.green(`${name} is online — it will DM you an introduction shortly.`));
1216
- else
1217
- console.log(chalk.yellow(`${name} has not reported running yet; check hq bot logs ${name}`));
1218
- return;
1219
- }
1220
- await startBot(name, dir, config, opts.json ? "json" : "text", { ...(isSetupBot ? { ok: true } : {}), ...created_, daemon: daemonMessage });
1221
- return;
1222
1297
  }
1223
1298
  if (opts.json) {
1224
- console.log(JSON.stringify({ ok: true, name, ...created_, daemon: daemonMessage, running: false }, null, 2));
1299
+ const status = readBotStatus(dir);
1300
+ console.log(JSON.stringify({
1301
+ ok: true,
1302
+ name,
1303
+ ...created_,
1304
+ daemon: null,
1305
+ running: fleetBootstrap.running,
1306
+ how: fleetBootstrap.running ? "started by the HQ Fleet Runtime" : null,
1307
+ state: status?.state ?? "unknown",
1308
+ pid: status?.pid ?? null,
1309
+ lastError: status?.lastError ?? null,
1310
+ }, null, 2));
1225
1311
  }
1226
1312
  }
1227
1313
  // ── companies ────────────────────────────────────────────────────────────────
@@ -1345,12 +1431,20 @@ async function runBotResident(nameArg) {
1345
1431
  // A bot with no company (the personal bot first-run setup creates) syncs the
1346
1432
  // HQ's active company. Only one local bot registers the job per company, and
1347
1433
  // the first sync starts in the background right after the job is created.
1348
- const runHotSignalSync = (input) => runHotSignalSyncJob({
1434
+ // US-006: a fired job runs the signals engine (garden -> adapters ->
1435
+ // extract -> garden -> upload) and replies in the storyboarded format.
1436
+ const runHotSignalSync = (input) =>
1437
+ // The engine is gated default-off (cli.bot-signals-engine-job); with the
1438
+ // flag off the job runs the hot-channel sync alone, as before US-006.
1439
+ signalsEngineForJob(runSignals).then((signals) => runHotSignalSyncJob({
1349
1440
  ...input,
1441
+ hqRoot: config.hqRoot,
1442
+ timeZone: input.timeZone ?? machineTimeZone(),
1443
+ ...(signals ? { signals } : {}),
1350
1444
  sync: (syncInput) => runCompanyHotChannelSync({ hqRoot: config.hqRoot, ...syncInput }),
1351
1445
  // A new pass per run: the login preflight and the 20-document cap are per run.
1352
1446
  signalsPass: createClaudeSignalsPass({ hqRoot: config.hqRoot, company: input.company }),
1353
- });
1447
+ }));
1354
1448
  let otherBotDirs = [];
1355
1449
  try {
1356
1450
  otherBotDirs = listBotNames(botsRoot()).map((n) => botDir(n));
@@ -1374,7 +1468,7 @@ async function runBotResident(nameArg) {
1374
1468
  api,
1375
1469
  runtime: runtimeFor(config.runtime),
1376
1470
  ownerContext: () => owner.get(),
1377
- hotSignalSync: ({ company, catchUp, now, sendAlert }) => runHotSignalSync({ company, catchUp, now, sendAlert }),
1471
+ hotSignalSync: (input) => runHotSignalSync(input),
1378
1472
  // US-005: decision picks in the owner DM, with the bot's own machine token
1379
1473
  // (never the owner's login).
1380
1474
  decisions: new DecisionsClient(token),
@@ -1446,8 +1540,9 @@ async function runBotStatus(nameArg, opts) {
1446
1540
  await decorateOnline([row]);
1447
1541
  const status = readBotStatus(dir);
1448
1542
  const creds = readBotCredsIdentity(dir);
1543
+ const channels = fleet ? fleetLocalChannels(readFleetLocalGatewayState(config.agentUid), row.processAlive) : null;
1449
1544
  if (opts.json) {
1450
- console.log(JSON.stringify({ ...row, ...(fleet ? { engine: config.engine } : {}), credsPresent: fleet || !!creds, login: describeBotAuth(dir, config), status }, null, 2));
1545
+ console.log(JSON.stringify({ ...row, ...(fleet ? { engine: config.engine, channels } : {}), credsPresent: fleet || !!creds, login: describeBotAuth(dir, config), status }, null, 2));
1451
1546
  return;
1452
1547
  }
1453
1548
  console.log(`${chalk.bold(name)} ${row.agentUid}`);
@@ -1473,6 +1568,8 @@ async function runBotStatus(nameArg, opts) {
1473
1568
  console.log(` answering: ${row.busy ? chalk.green(`yes${row.busySince ? `, since ${row.busySince}` : ""}`) : chalk.gray("no")}`);
1474
1569
  if (fleet) {
1475
1570
  console.log(` service: ${fleetLocalServiceInstalled(config.agentUid) ? "installed (HQ Fleet gateway, starts at login)" : row.serviceInstallPending ? "not installed yet; installs on hq bot start" : "not installed"}`);
1571
+ if (channels)
1572
+ console.log(` channels: Slack ${channels.slack.replace(/_/g, " ")}, Telegram ${channels.telegram.replace(/_/g, " ")}`);
1476
1573
  }
1477
1574
  else {
1478
1575
  console.log(` launchd: ${row.daemonInstalled ? (row.daemonLoaded ? "installed, loaded" : "installed, not loaded") : "not installed"}`);
@@ -2129,10 +2226,38 @@ export async function runBotIntro(nameArg) {
2129
2226
  export function botExists(name) {
2130
2227
  return readBotConfig(botDir(name)) !== null;
2131
2228
  }
2229
+ export async function updateFleetRuntimeBot(nameArg, opts = {}) {
2230
+ const name = validateBotName(nameArg);
2231
+ return withBotLock(name, async () => {
2232
+ const b = requireBot(name);
2233
+ if (!isFleetRuntimeBot(b.config) || hasPromotionHold(b.dir))
2234
+ return "unchanged";
2235
+ const adopted = await adoptFleetRuntimeProfile(b.name, b.dir, b.config, { pointerOnly: true });
2236
+ if (!adopted && b.config.fleetRestartPending !== true)
2237
+ return "unchanged";
2238
+ const config = readBotConfig(b.dir) ?? b.config;
2239
+ if (adopted) {
2240
+ refreshFleetLocalInstructions(config.agentUid, fleetBotSystemPrompt(b.name, config));
2241
+ syncFleetRuntimeJobs(b.name, b.dir, config);
2242
+ }
2243
+ if (opts.busy?.()) {
2244
+ patchBotConfig(b.dir, { fleetRestartPending: true });
2245
+ return "waiting";
2246
+ }
2247
+ const result = controlFleetLocalBot(config.agentUid, "install");
2248
+ if (result.ok !== true) {
2249
+ patchBotConfig(b.dir, { fleetRestartPending: true });
2250
+ throw new Error(typeof result.summary === "string" && result.summary ? result.summary : "the HQ Fleet gateway did not restart");
2251
+ }
2252
+ patchBotConfig(b.dir, { fleetServiceInstallPending: false, fleetRestartPending: false, enabled: true });
2253
+ return "updated";
2254
+ }, { command: "hq daemon service fleet-bots-update" });
2255
+ }
2132
2256
  /** `hq bot restart <name>`: stop, then start (launchd when installed). */
2133
2257
  export async function runBotRestart(nameArg, opts = {}) {
2134
2258
  const b = requireBot(nameArg);
2135
2259
  if (isFleetRuntimeBot(b.config)) {
2260
+ refuseHeldFleetBot(b.dir);
2136
2261
  const adopted = await adoptFleetRuntimeProfile(b.name, b.dir, b.config);
2137
2262
  runFleetRuntimeControl(b.name, b.config, adopted ? "install" : "restart", opts.json === true || opts.output === "json");
2138
2263
  return;
@@ -2370,7 +2495,7 @@ export function configFromLocalConfig(input) {
2370
2495
  ...(kind === "company" ? { companies: local?.companies ?? [] } : {}),
2371
2496
  ...(model ? { model } : {}),
2372
2497
  ...(effort ? { effort } : {}),
2373
- ...(local?.autoApprove === false ? { autoApprove: false } : {}),
2498
+ ...(local?.autoApprove === true ? { autoApprove: true } : {}),
2374
2499
  hqRoot: input.hqRoot,
2375
2500
  workerDir,
2376
2501
  ...(workerSource === "worker" && workerId ? { workerSource, workerId } : {}),
@@ -2469,7 +2594,7 @@ async function adoptRemoteBotLocked(input) {
2469
2594
  systemPrompt: fleetBotSystemPrompt(name, config),
2470
2595
  provider: fleetProviderForRuntime(config.runtime, config.model),
2471
2596
  ownerCompanies: await fleetOwnerCompanies(),
2472
- ...(config.autoApprove === false ? { autoApprove: false } : {}),
2597
+ ...(config.autoApprove === true ? { autoApprove: true } : {}),
2473
2598
  ...(input.preserveFirstStart
2474
2599
  ? {
2475
2600
  firstStart: {
@@ -2786,20 +2911,18 @@ export function registerBotCommand(program) {
2786
2911
  .description("Provision a local bot identity, scaffold its worker folder, install its launchd agent, and start it")
2787
2912
  .option("--kind <kind>", `personal (acts as you, everywhere you are) or company (acts as itself, as a member of its companies). Default: personal, or company when created from a company worker`)
2788
2913
  .option("--company <slug>", "Company bots: a company the bot is a member of (repeat for several); the owner invites it on creation", collectRepeated, [])
2789
- .option("--runtime <runtime>", `Model CLI to run the bot with: ${BOT_RUNTIMES.join("|")}`)
2790
- .option("--provider <provider>", `HQ Fleet provider: ${BOT_RUNTIMES.join("|")} (interactive Fleet creation prompts when omitted)`)
2791
- .option("--hq-fleet-runtime", "Create this personal bot with the local HQ Fleet Runtime")
2792
- .option("--legacy-runtime", "Run this bot with the model CLI even where the HQ Fleet Runtime is the default")
2793
- .option("--model <model>", "Model override passed to the runtime CLI")
2794
- .option("--effort <level>", `Thinking level: claude ${BOT_EFFORT_LEVELS.claude.join("|")}; codex ${BOT_EFFORT_LEVELS.codex.join("|")}; grok ${BOT_EFFORT_LEVELS.grok.join("|")} (default ${DEFAULT_BOT_EFFORT})`)
2795
- .option("--no-auto-approve", "Keep the runtime's permission gates (default: everything pre-approved; a headless bot cannot answer prompts)")
2914
+ .option("--runtime <runtime>", `Model provider the bot answers with: ${BOT_RUNTIMES.join("|")} (same as --provider; default claude)`)
2915
+ .option("--provider <provider>", `HQ Fleet provider: ${BOT_RUNTIMES.join("|")} (default claude)`)
2916
+ .option("--hq-fleet-runtime", "Accepted for compatibility; every new bot runs on the HQ Fleet Runtime")
2917
+ .option("--legacy-runtime", "No longer supported; new bots always run on the HQ Fleet Runtime")
2918
+ .option("--model <model>", "Model the bot answers with")
2919
+ .option("--auto-approve", "Turn the runtime's command approval prompts off for every sender (default: prompts stay on; the owner's own turns are always allowed)")
2796
2920
  .option("--worker <workerId>", "Run an existing company or core worker (see hq bot workers): its instructions, skills, context, and company policies; a company worker binds the bot to that company on every turn")
2797
2921
  .option("--display-name <name>", `The human name people see for the bot in HQ (e.g. Maya; up to ${BOT_DISPLAY_NAME_MAX_CHARS} chars). The handle stays <name>`)
2798
2922
  .option("--intro <text>", `The hello the bot DMs you when it first comes online (verbatim, up to ${BOT_INTRO_MAX_CHARS} chars)`)
2799
2923
  .option("--kickoff <prompt>", `A first task the bot starts on by itself right after its hello: one model turn run as if you had sent it, answered in your DM, once (up to ${BOT_KICKOFF_MAX_CHARS} chars)`)
2800
2924
  .option("--memory <where>", "Where the bot keeps its memory: synced (personal/workers/<name>/memory in HQ) or local (~/.hq/bots/<name>/memory, this computer only)", "synced")
2801
- .option("--lean", "Claude bots: run each turn without this computer's plugins, MCP servers and HQ's session hooks (HQ's tool hooks still run), so replies take seconds")
2802
- .option("--no-daemon", "Do not install the launchd agent (macOS)")
2925
+ .option("--no-daemon", "Accepted for compatibility; HQ Fleet bots run under their own background service")
2803
2926
  .option("--no-start", "Create without starting")
2804
2927
  .option("--json", "Print machine-readable JSON")
2805
2928
  .action((name, opts) => wrap(() => runBotCreate(name, opts))());
@@ -2846,6 +2969,7 @@ export function registerBotCommand(program) {
2846
2969
  .action((name, opts) => wrap(async () => {
2847
2970
  const b = requireBot(name);
2848
2971
  if (isFleetRuntimeBot(b.config)) {
2972
+ refuseHeldFleetBot(b.dir);
2849
2973
  const adopted = await adoptFleetRuntimeProfile(b.name, b.dir, b.config);
2850
2974
  runFleetRuntimeControl(b.name, b.config, adopted ? "install" : "start", opts.json === true);
2851
2975
  return;
@@ -2859,6 +2983,7 @@ export function registerBotCommand(program) {
2859
2983
  .action((name, opts) => wrap(async () => {
2860
2984
  const b = requireBot(name);
2861
2985
  if (isFleetRuntimeBot(b.config)) {
2986
+ refuseHeldFleetBot(b.dir);
2862
2987
  runFleetRuntimeControl(b.name, b.config, "stop", opts.json === true);
2863
2988
  return;
2864
2989
  }