@indigoai-us/hq-cli 5.345.191 → 5.345.193
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 +398 -0
- package/assets/bot-workers/setup/worker.yaml +91 -34
- package/dist/command-catalog.generated.d.ts +96 -19
- package/dist/command-catalog.generated.js +124 -21
- package/dist/commands/bot-jobs.d.ts +21 -18
- package/dist/commands/bot-jobs.js +127 -39
- package/dist/commands/bot.d.ts +4 -3
- package/dist/commands/bot.js +265 -140
- package/dist/commands/core-jobs-schedule.js +10 -0
- package/dist/commands/daemon.js +37 -0
- package/dist/commands/files.d.ts +41 -6
- package/dist/commands/files.js +108 -27
- package/dist/commands/pack-start-bot.d.ts +4 -6
- package/dist/commands/pack-start-bot.js +4 -8
- package/dist/commands/signals-run.d.ts +45 -0
- package/dist/commands/signals-run.js +175 -0
- package/dist/commands/signals.d.ts +1 -0
- package/dist/commands/signals.js +3 -0
- package/dist/commands/sources.d.ts +11 -0
- package/dist/commands/sources.js +137 -25
- package/dist/commands/staff/reset-test-account.d.ts +10 -0
- package/dist/commands/staff/reset-test-account.js +18 -2
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +31171 -52512
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- package/dist/lib/bot/api.d.ts +3 -2
- package/dist/lib/bot/config.d.ts +2 -0
- package/dist/lib/bot/fleet-cron.d.ts +121 -0
- package/dist/lib/bot/fleet-cron.js +319 -0
- package/dist/lib/bot/fleet-local.d.ts +93 -1
- package/dist/lib/bot/fleet-local.js +546 -34
- package/dist/lib/bot/fleet-owner-companies.d.ts +12 -0
- package/dist/lib/bot/fleet-owner-companies.js +30 -0
- package/dist/lib/bot/fleet-watch.d.ts +25 -0
- package/dist/lib/bot/fleet-watch.js +51 -0
- package/dist/lib/bot/index.d.ts +1 -1
- package/dist/lib/bot/index.js +1 -1
- package/dist/lib/bot/job-run.d.ts +5 -4
- package/dist/lib/bot/job-run.js +5 -4
- package/dist/lib/bot/jobs/hot-signal-sync.d.ts +57 -3
- package/dist/lib/bot/jobs/hot-signal-sync.js +163 -4
- package/dist/lib/bot/local-jobs.d.ts +13 -2
- package/dist/lib/bot/local-jobs.js +17 -4
- package/dist/lib/bot/progress.d.ts +14 -0
- package/dist/lib/bot/progress.js +47 -1
- package/dist/lib/bot/run.d.ts +55 -2
- package/dist/lib/bot/run.js +192 -10
- package/dist/lib/bot/setup-tool-offer.d.ts +1 -0
- package/dist/lib/bot/setup-tool-offer.js +16 -0
- package/dist/lib/bot/signals-engine-flag.d.ts +18 -0
- package/dist/lib/bot/signals-engine-flag.js +30 -0
- package/dist/lib/daemon/catalog.d.ts +4 -0
- package/dist/lib/daemon/catalog.js +29 -9
- package/dist/lib/daemon/legacy.d.ts +6 -0
- package/dist/lib/daemon/legacy.js +16 -10
- package/dist/lib/daemon/run.d.ts +1 -0
- package/dist/lib/daemon/run.js +10 -0
- package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
- package/dist/lib/flag-registry-worker-source.generated.js +1 -1
- package/dist/lib/flag-registry.d.ts +13 -1
- package/dist/lib/flag-registry.js +46 -0
- package/dist/lib/hot-channels/adapters/attio.d.ts +75 -2
- package/dist/lib/hot-channels/adapters/attio.js +367 -29
- package/dist/lib/hot-channels/adapters/gmail.d.ts +14 -0
- package/dist/lib/hot-channels/adapters/gmail.js +59 -2
- package/dist/lib/hot-channels/adapters/linear.d.ts +24 -0
- package/dist/lib/hot-channels/adapters/linear.js +71 -1
- package/dist/lib/hot-channels/adapters/meetings.js +4 -1
- package/dist/lib/hot-channels/adapters/slack.js +7 -3
- package/dist/lib/hot-channels/alert.js +1 -1
- package/dist/lib/hot-channels/contract.d.ts +11 -0
- package/dist/lib/hot-channels/contract.js +1 -0
- package/dist/lib/hot-channels/cursors.js +1 -1
- package/dist/lib/hot-channels/landing.d.ts +6 -2
- package/dist/lib/hot-channels/landing.js +13 -6
- package/dist/lib/hot-channels/ledger.js +2 -0
- package/dist/lib/hot-channels/loop.js +18 -4
- package/dist/lib/hot-channels/registry.js +9 -1
- package/dist/lib/hot-channels/run.d.ts +18 -0
- package/dist/lib/hot-channels/run.js +15 -4
- package/dist/lib/hot-channels/signals-pass.d.ts +1 -0
- package/dist/lib/hot-channels/signals-pass.js +7 -5
- package/dist/lib/hot-channels/sync-lock.js +22 -6
- package/dist/lib/mesh/live/daemon/install.d.ts +22 -0
- package/dist/lib/mesh/live/daemon/install.js +117 -30
- package/dist/lib/signals/adapters-catalog.d.ts +77 -0
- package/dist/lib/signals/adapters-catalog.js +194 -0
- package/dist/lib/signals/engine.d.ts +196 -0
- package/dist/lib/signals/engine.js +495 -0
- package/dist/lib/signals/failure-alert.d.ts +89 -0
- package/dist/lib/signals/failure-alert.js +175 -0
- package/dist/lib/signals/local-date.d.ts +3 -0
- package/dist/lib/signals/local-date.js +6 -0
- package/dist/lib/signals/projection.d.ts +42 -0
- package/dist/lib/signals/projection.js +67 -0
- package/dist/lib/signals/reply.d.ts +112 -0
- package/dist/lib/signals/reply.js +368 -0
- package/dist/lib/signals/run-lock.d.ts +36 -0
- package/dist/lib/signals/run-lock.js +148 -0
- package/dist/lib/signals/run-record.d.ts +24 -0
- package/dist/lib/signals/run-record.js +82 -0
- package/dist/lib/signals/sanitize.d.ts +105 -0
- package/dist/lib/signals/sanitize.js +439 -0
- package/dist/lib/signals/settings.d.ts +10 -0
- package/dist/lib/signals/settings.js +48 -0
- package/dist/lib/signals/steps/adapters.d.ts +14 -0
- package/dist/lib/signals/steps/adapters.js +156 -0
- package/dist/lib/signals/steps/entity-similarity.d.ts +10 -0
- package/dist/lib/signals/steps/entity-similarity.js +47 -0
- package/dist/lib/signals/steps/extract.d.ts +113 -0
- package/dist/lib/signals/steps/extract.js +765 -0
- package/dist/lib/signals/steps/garden.d.ts +24 -0
- package/dist/lib/signals/steps/garden.js +250 -0
- package/dist/lib/signals/steps/types.d.ts +112 -0
- package/dist/lib/signals/steps/types.js +4 -0
- package/dist/lib/signals/steps/upload.d.ts +43 -0
- package/dist/lib/signals/steps/upload.js +324 -0
- package/dist/lib/signals/write-guard.d.ts +63 -0
- package/dist/lib/signals/write-guard.js +287 -0
- package/dist/utils/resident-package-preload.js +16 -0
- package/package.json +9 -9
- package/dist/lib/bot/fleet-default-flag.d.ts +0 -31
- package/dist/lib/bot/fleet-default-flag.js +0 -127
package/dist/commands/bot.js
CHANGED
|
@@ -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] [--
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 ===
|
|
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
|
-
|
|
811
|
-
|
|
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
|
|
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 (
|
|
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
|
|
892
|
-
|
|
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
|
-
|
|
1113
|
+
engine: fleetInstallation.engine,
|
|
1007
1114
|
kind,
|
|
1008
1115
|
...(kind === "company" ? { companies } : {}),
|
|
1009
1116
|
...(model ? { model } : {}),
|
|
1010
|
-
...(
|
|
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
|
-
|
|
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
|
-
...(
|
|
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,
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
const
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
const
|
|
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
|
|
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:
|
|
1177
|
-
|
|
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
|
-
|
|
1194
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: (
|
|
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 ===
|
|
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 ===
|
|
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
|
|
2790
|
-
.option("--provider <provider>", `HQ Fleet provider: ${BOT_RUNTIMES.join("|")} (
|
|
2791
|
-
.option("--hq-fleet-runtime", "
|
|
2792
|
-
.option("--legacy-runtime", "
|
|
2793
|
-
.option("--model <model>", "Model
|
|
2794
|
-
.option("--
|
|
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("--
|
|
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
|
}
|