@indigoai-us/hq-cli 5.345.198 → 5.345.199

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 (102) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/command-catalog.generated.d.ts +106 -6
  3. package/dist/command-catalog.generated.js +130 -6
  4. package/dist/commands/bot-events.d.ts +19 -0
  5. package/dist/commands/bot-events.js +46 -0
  6. package/dist/commands/bot-jobs.d.ts +2 -0
  7. package/dist/commands/bot-jobs.js +43 -2
  8. package/dist/commands/bot.d.ts +5 -0
  9. package/dist/commands/bot.js +33 -1
  10. package/dist/commands/lanes-report.d.ts +2 -1
  11. package/dist/commands/lanes-report.js +44 -1
  12. package/dist/commands/lanes.d.ts +9 -0
  13. package/dist/commands/lanes.js +95 -9
  14. package/dist/commands/meetings.js +1 -21
  15. package/dist/commands/signals-run.d.ts +2 -0
  16. package/dist/commands/signals-run.js +6 -1
  17. package/dist/lanes-drain-hook.js +9 -0
  18. package/dist/lib/bot/api.d.ts +5 -0
  19. package/dist/lib/bot/claude-login.d.ts +31 -0
  20. package/dist/lib/bot/claude-login.js +160 -0
  21. package/dist/lib/bot/events.d.ts +18 -0
  22. package/dist/lib/bot/events.js +160 -0
  23. package/dist/lib/bot/executive-settings.d.ts +10 -0
  24. package/dist/lib/bot/executive-settings.js +43 -0
  25. package/dist/lib/bot/fleet-cron.js +4 -0
  26. package/dist/lib/bot/fleet-local.js +10 -10
  27. package/dist/lib/bot/job-run.js +1 -1
  28. package/dist/lib/bot/jobs/hot-signal-sync.d.ts +8 -0
  29. package/dist/lib/bot/jobs/hot-signal-sync.js +2 -0
  30. package/dist/lib/bot/jobs/meeting-detect.d.ts +28 -0
  31. package/dist/lib/bot/jobs/meeting-detect.js +176 -0
  32. package/dist/lib/bot/jobs/meeting-rider.d.ts +53 -0
  33. package/dist/lib/bot/jobs/meeting-rider.js +284 -0
  34. package/dist/lib/bot/jobs/signal-to-lane.d.ts +32 -0
  35. package/dist/lib/bot/jobs/signal-to-lane.js +169 -0
  36. package/dist/lib/bot/local-jobs.d.ts +15 -2
  37. package/dist/lib/bot/local-jobs.js +24 -7
  38. package/dist/lib/bot/run.d.ts +7 -49
  39. package/dist/lib/bot/run.js +100 -4
  40. package/dist/lib/bot/status.d.ts +21 -0
  41. package/dist/lib/doctor/checks/bots.d.ts +6 -0
  42. package/dist/lib/doctor/checks/bots.js +47 -1
  43. package/dist/lib/hot-channels/adapters/meetings.d.ts +1 -0
  44. package/dist/lib/hot-channels/adapters/meetings.js +3 -1
  45. package/dist/lib/hot-channels/meeting-source-ref.d.ts +3 -0
  46. package/dist/lib/hot-channels/meeting-source-ref.js +7 -0
  47. package/dist/lib/hot-channels/schedule.js +2 -2
  48. package/dist/lib/hot-channels/signals-pass.d.ts +9 -32
  49. package/dist/lib/hot-channels/signals-pass.js +51 -17
  50. package/dist/lib/lanes/admission.d.ts +2 -0
  51. package/dist/lib/lanes/admission.js +4 -0
  52. package/dist/lib/lanes/approval-gate.js +4 -1
  53. package/dist/lib/lanes/links.d.ts +8 -1
  54. package/dist/lib/lanes/links.js +84 -3
  55. package/dist/lib/lanes/outcome.d.ts +154 -0
  56. package/dist/lib/lanes/outcome.js +151 -0
  57. package/dist/lib/lanes/reconcile.js +1 -0
  58. package/dist/lib/lanes/signal-brief-template.d.ts +7 -0
  59. package/dist/lib/lanes/signal-brief-template.js +40 -0
  60. package/dist/lib/lanes/signal-lane.d.ts +24 -0
  61. package/dist/lib/lanes/signal-lane.js +99 -0
  62. package/dist/lib/lanes/source-report.d.ts +37 -0
  63. package/dist/lib/lanes/source-report.js +66 -0
  64. package/dist/lib/lanes/spawn.d.ts +6 -0
  65. package/dist/lib/lanes/spawn.js +21 -0
  66. package/dist/lib/lanes/store.d.ts +3 -0
  67. package/dist/lib/lanes/store.js +64 -0
  68. package/dist/lib/lanes/story-ledger.d.ts +3 -0
  69. package/dist/lib/lanes/story-ledger.js +1 -0
  70. package/dist/lib/lanes/types.d.ts +137 -0
  71. package/dist/lib/lanes/types.js +17 -2
  72. package/dist/lib/lanes/watcher.js +4 -0
  73. package/dist/lib/live-meetings.d.ts +19 -0
  74. package/dist/lib/live-meetings.js +23 -0
  75. package/dist/lib/mesh/live/daemon/doctor.js +6 -7
  76. package/dist/lib/mesh/live/spool.d.ts +8 -0
  77. package/dist/lib/mesh/live/spool.js +59 -40
  78. package/dist/lib/plan-limit-nag.d.ts +30 -4
  79. package/dist/lib/plan-limit-nag.js +68 -17
  80. package/dist/lib/signals/engine.d.ts +4 -0
  81. package/dist/lib/signals/engine.js +25 -5
  82. package/dist/lib/signals/incremental.d.ts +42 -0
  83. package/dist/lib/signals/incremental.js +191 -0
  84. package/dist/lib/signals/prior-signals.d.ts +11 -0
  85. package/dist/lib/signals/prior-signals.js +44 -0
  86. package/dist/lib/signals/reply.js +6 -1
  87. package/dist/lib/signals/settings.d.ts +5 -0
  88. package/dist/lib/signals/settings.js +17 -0
  89. package/dist/lib/signals/steps/extract.d.ts +23 -2
  90. package/dist/lib/signals/steps/extract.js +142 -9
  91. package/dist/lib/signals/steps/types.d.ts +1 -0
  92. package/dist/lib/signals/write-guard.js +7 -0
  93. package/dist/utils/cognito-callback-port.d.ts +15 -0
  94. package/dist/utils/cognito-callback-port.js +51 -0
  95. package/dist/utils/cognito-config.js +2 -3
  96. package/dist/utils/cognito-session.d.ts +7 -6
  97. package/dist/utils/cognito-session.js +14 -14
  98. package/dist/utils/interactive-caller.d.ts +15 -0
  99. package/dist/utils/interactive-caller.js +70 -0
  100. package/dist/utils/login-browser.js +3 -1
  101. package/dist/utils/resident-package-preload.js +4 -0
  102. package/package.json +7 -7
@@ -43,6 +43,8 @@ import { createVaultObjectDownloader } from "../lib/agent-kit/attachment-downloa
43
43
  import { addBotToCompanies, describeCompanyFailures, removeBotFromCompanies } from "./bot-companies.js";
44
44
  import { registerBotContinuityCommand } from "./bot-continuity.js";
45
45
  import { registerBotJobsCommand } from "./bot-jobs.js";
46
+ import { registerBotEventsCommand } from "./bot-events.js";
47
+ import { executiveStatus } from "../lib/bot/executive-settings.js";
46
48
  import { ensureFleetHotSignalSync, fleetCronOps, migrateLegacyJobsToFleet } from "../lib/bot/fleet-cron.js";
47
49
  import { resolvePackSetupPrompt } from "../lib/bot/pack-setup.js";
48
50
  import { detectCodingToolActivity } from "../lib/bot/coding-tool-activity.js";
@@ -63,6 +65,8 @@ import { daemonPaths } from "../lib/daemon/paths.js";
63
65
  import { launchdLabel } from "../lib/bot/paths.js";
64
66
  import { isAncestorProcess } from "../lib/bot/self-process.js";
65
67
  import { runtimeDisplayName, signInCommand } from "../lib/bot/runtime-sign-in.js";
68
+ import { checkClaudeCodeLogin, claudeLoginFix, describeClaudeLoginSources } from "../lib/bot/claude-login.js";
69
+ import { unresolvedLastFailure } from "../lib/doctor/checks/bots.js";
66
70
  import { classifyRuntimeFailure, runtimeFailureReason } from "../lib/bot/runtime-failure.js";
67
71
  import { PROBE_DEFAULT_TIMEOUT_MS, probeBotRuntime } from "../lib/bot/probe.js";
68
72
  import { BOT_LOGIN_RUNTIMES, botAuthEnv, botRuntimeAuth, capacityAccountHome, describeBotAuth, formatBotAuth, isBotLoginRuntime, restoreBotSessions, setAsideBotSessions, signInBotRuntime, signOutBotRuntime, withRuntimeAuth, } from "../lib/bot/runtime-auth.js";
@@ -774,6 +778,22 @@ async function installFleetRuntimeProfile(name, dir, config, opts = {}) {
774
778
  * "unreadable output: trailing characters").
775
779
  */
776
780
  /** Seams for the self-stop path, replaced in tests. */
781
+ export const botCreateSeams = {
782
+ /** The Claude Code login read the way the HQ Fleet Runtime reads it. */
783
+ claudeLogin: () => checkClaudeCodeLogin(),
784
+ };
785
+ /**
786
+ * A Claude bot on the HQ Fleet Runtime answers with the owner's Claude Code
787
+ * login. Creating one on a computer with no readable login provisions an
788
+ * identity that can only ever answer "I'm away for a bit", so refuse first
789
+ * and say which sources were checked.
790
+ */
791
+ function requireClaudeCodeLoginForCreate(name) {
792
+ const check = botCreateSeams.claudeLogin();
793
+ if (check.signedIn)
794
+ return;
795
+ throw Object.assign(new Error(`Claude Code is not signed in on this computer, so ${name} could not answer. ${describeClaudeLoginSources(check)} ${claudeLoginFix()}`.replace(/\s+/g, " ").trim()), { expected: true });
796
+ }
777
797
  export const botStopSeams = {
778
798
  /** True when this process runs inside the bot process `botPid` (one of its turns). */
779
799
  isInsideBot: (botPid) => isAncestorProcess(botPid),
@@ -1114,6 +1134,8 @@ async function runBotCreateLocked(name, opts) {
1114
1134
  }
1115
1135
  const fleetProvider = fleetProviderForRuntime(runtime, model);
1116
1136
  const fleetAuthPending = fleetProviderNeedsSignIn(fleetProvider);
1137
+ if (fleetProvider.id === "anthropic")
1138
+ requireClaudeCodeLoginForCreate(name);
1117
1139
  const token = await ownerToken();
1118
1140
  const owner = await resolveCallerPersonUid(token, undefined, { command: "hq bot create" });
1119
1141
  if (!owner)
@@ -1664,7 +1686,7 @@ async function runBotStatus(nameArg, opts) {
1664
1686
  const creds = readBotCredsIdentity(dir);
1665
1687
  const channels = fleet ? fleetLocalChannels(readFleetLocalGatewayState(config.agentUid), row.processAlive) : null;
1666
1688
  if (opts.json) {
1667
- console.log(JSON.stringify({ ...row, ...(fleet ? { engine: config.engine, channels } : {}), credsPresent: fleet || !!creds, login: describeBotAuth(dir, config), status }, null, 2));
1689
+ console.log(JSON.stringify({ ...row, executive: executiveStatus(config, dir), ...(fleet ? { engine: config.engine, channels } : {}), credsPresent: fleet || !!creds, login: describeBotAuth(dir, config), status }, null, 2));
1668
1690
  return;
1669
1691
  }
1670
1692
  console.log(`${chalk.bold(name)} ${row.agentUid}`);
@@ -1733,6 +1755,15 @@ async function runBotStatus(nameArg, opts) {
1733
1755
  const cls = classifyRuntimeFailure(status.lastError);
1734
1756
  console.log(` last problem: ${chalk.yellow(`${cls}: ${runtimeFailureReason(cls, row.runtime)}`)} ${chalk.dim(`(details: hq bot logs ${name})`)}`);
1735
1757
  }
1758
+ if (status.lastFailure) {
1759
+ const failure = status.lastFailure;
1760
+ const resolved = unresolvedLastFailure(status) === null;
1761
+ const paint = resolved ? chalk.dim : chalk.yellow;
1762
+ console.log(` last failure: ${paint(`${failure.at} (${failure.class}${resolved ? ", answered since" : ""})`)}`);
1763
+ console.log(` cause: ${paint(failure.cause)}`);
1764
+ console.log(` fix: ${paint(failure.fix)}`);
1765
+ console.log(` report: ${failure.uploaded && failure.reportId ? `${failure.reportId} (in HQ, with the bot's last log lines)` : chalk.red("not uploaded to HQ")} ${chalk.dim(`owner DM ${failure.ownerNotified ? "sent" : "not sent"}`)}`);
1766
+ }
1736
1767
  }
1737
1768
  console.log(chalk.dim(` state dir: ${dir}`));
1738
1769
  }
@@ -3052,6 +3083,7 @@ export function registerBotCommand(program) {
3052
3083
  .description("Personal local bots that run on this computer with your own model login");
3053
3084
  registerBotContinuityCommand(bot);
3054
3085
  registerBotJobsCommand(bot, { requireBot, botTokenSupplier });
3086
+ registerBotEventsCommand(bot, { requireBot });
3055
3087
  bot.command("promote <name>")
3056
3088
  .description("Move this local bot to cloud compute, preserving its identity and private context")
3057
3089
  .requiredOption("--company <uid>", "Destination company identity")
@@ -1,9 +1,10 @@
1
1
  import type { Command } from "commander";
2
2
  import { type QuestionBody } from "../lib/lanes/questions.js";
3
- import type { LaneCompanyRef, QuestionRecord } from "../lib/lanes/types.js";
3
+ import type { LaneCompanyRef, LaneRecord, QuestionRecord } from "../lib/lanes/types.js";
4
4
  import { type LaneChecksDeps } from "./lanes-checks.js";
5
5
  type WrapHandler = <Args extends unknown[]>(action: (...args: Args) => Promise<void>) => (...args: Args) => Promise<void>;
6
6
  export interface LaneReportsDeps extends LaneChecksDeps {
7
+ laneRunsHere: (lane: LaneRecord) => boolean;
7
8
  readQuestionBody: (root: string, question: QuestionRecord) => QuestionBody;
8
9
  focusDashboardEnabled: (company: LaneCompanyRef) => Promise<boolean>;
9
10
  }
@@ -1,12 +1,15 @@
1
+ import * as fs from "node:fs";
1
2
  import * as path from "node:path";
2
3
  import { LanesError } from "../lib/lanes/errors.js";
3
4
  import { assertQuestionBodySecretSafe } from "../lib/lanes/questions.js";
4
- import { questionRecordPath } from "../lib/lanes/store.js";
5
+ import { listLanesBySource, questionRecordPath } from "../lib/lanes/store.js";
5
6
  import { normalizeIsoTimestamp } from "../lib/lanes/timestamp.js";
6
7
  import { mapSourceStoryStatus } from "../lib/mesh/story-status.js";
7
8
  import { buildTrackerOwnerReport, buildTrackerTeamPage, renderTrackerOwnerReportText, renderTrackerTeamPageText, } from "../lib/lanes/tracker-report.js";
8
9
  import { FIRST_TRACKER_REPORT_SINCE, readTrackerReportState, writeTrackerReportState, } from "../lib/lanes/tracker-report-state.js";
9
10
  import { listTrackerRecordsTolerant, loadCompanyQuestionRecords, loadProjectMappings, mappingIncludesStory, pathForTrackerRecord, readProject, resolveTrackerCompany, } from "./lanes-checks.js";
11
+ import { buildSourceReport, renderSourceReportText } from "../lib/lanes/source-report.js";
12
+ import { readLaneStatusRowFields } from "../lib/lanes/status-fields.js";
10
13
  function fail(error, message, details = {}) {
11
14
  throw new LanesError(error, message, details);
12
15
  }
@@ -331,10 +334,50 @@ export function registerLaneReportCommands(parent, deps, wrap) {
331
334
  parent.command("report")
332
335
  .description("Generate the owner report for tracker teams")
333
336
  .option("--company <slug>", "Select the company by slug")
337
+ .option("--source <ref>", "Report lanes for a source (requires --company; excludes --since)")
334
338
  .option("--since <iso>", "Report activity from this ISO timestamp")
335
339
  .option("--json", "Emit JSON instead of the owner report text")
336
340
  .action(wrap(async (options) => {
341
+ if (options.source !== undefined && (!options.company?.trim() || options.since !== undefined || !options.source.trim())) {
342
+ fail("source_report_options_invalid", "--source requires --company and a non-empty ref, and cannot be combined with --since");
343
+ }
337
344
  const company = await resolveTrackerCompany(deps, options.company);
345
+ if (options.source !== undefined) {
346
+ const root = deps.hqRoot();
347
+ const lanes = listLanesBySource(company.slug, options.source, root)
348
+ .filter(lane => lane.company?.uid === company.uid);
349
+ const ids = new Set(lanes.map(lane => lane.lane_id));
350
+ const questions = new Map();
351
+ if (lanes.length) {
352
+ const pending = loadCompanyQuestionRecords(root, company)
353
+ .filter(question => ids.has(question.lane_id) && question.status === "pending")
354
+ .sort((a, b) => a.asked_at.localeCompare(b.asked_at) || a.question_id.localeCompare(b.question_id));
355
+ for (const question of pending) {
356
+ if (questions.has(question.lane_id))
357
+ continue;
358
+ const body = deps.readQuestionBody(root, question);
359
+ assertQuestionBodySecretSafe(body);
360
+ questions.set(question.lane_id, { question_id: question.question_id, question: body.question });
361
+ }
362
+ }
363
+ const progress = new Map(lanes.filter(lane => lane.state === "running" || lane.state === "queued")
364
+ .map(lane => [lane.lane_id, readLaneStatusRowFields(root, lane, deps.now().getTime(), deps.laneRunsHere(lane)).last_line ?? "No progress recorded yet."]));
365
+ const report = buildSourceReport(company.slug, options.source, lanes, { questions, progress });
366
+ if (options.json)
367
+ console.log(JSON.stringify(report, null, 2));
368
+ else {
369
+ let template;
370
+ try {
371
+ template = fs.readFileSync(path.join(root, "personal/settings/executive/report.md"), "utf8");
372
+ }
373
+ catch (error) {
374
+ if (error.code !== "ENOENT")
375
+ throw error;
376
+ }
377
+ console.log(renderSourceReportText(report, template));
378
+ }
379
+ return;
380
+ }
338
381
  const root = deps.hqRoot();
339
382
  const generatedAt = deps.now().toISOString();
340
383
  const focusDashboardEnabled = await deps.focusDashboardEnabled(company);
@@ -165,6 +165,15 @@ export declare function pickupFailureUpdater(reasonRaw: string, details: Record<
165
165
  */
166
166
  export declare function applyOutpostAccountPickup(hqRoot: string, laneId: string, modelDeps?: KnownModelsDeps): LaneRecord;
167
167
  export declare function cancelPendingQuestionsForLane(hqRoot: string, laneId: string, nowIso: string, operatorReason?: string): QuestionRecord[];
168
+ /** In-process signal entry point sharing CLI admission, dedupe and conductor rules. */
169
+ export declare function createLaneFromSignal(input: {
170
+ hqRoot: string;
171
+ company: string;
172
+ signal_id: string;
173
+ }, deps?: LanesCommandDeps): Promise<{
174
+ lane_id: string;
175
+ existing: boolean;
176
+ }>;
168
177
  /** @internal Exported for regression tests of the profile-refresh company check. */
169
178
  export declare function applyResumeProfileAndEffort(hqRoot: string, current: LaneRecord, next: LaneRecord, input: {
170
179
  profile?: ResolvedResumeProfile;
@@ -75,6 +75,7 @@ import { resolveActiveMembershipCompany } from "../lib/mesh/api.js";
75
75
  import { openMeshTransport, requireToken } from "../lib/mesh/client.js";
76
76
  import { CompanySelectionError } from "../utils/company-selection-error.js";
77
77
  import { assertNoSecretText } from "../lib/lanes/text-scan.js";
78
+ import { createSignalLane } from "../lib/lanes/signal-lane.js";
78
79
  import { createLaneMesh, emitLaneDecisionEvents, emitLaneTerminalStateEvent, laneMeshEventIdentity, } from "../lib/lanes/mesh-events.js";
79
80
  import { sessionIdFromEnv } from "../lib/mesh/live/session-identity.js";
80
81
  import { applyLoopDisposition, loopDispositionSnapshot } from "../lib/lanes/loop-disposition.js";
@@ -285,7 +286,7 @@ function failJson(err) {
285
286
  process.stderr.write(`hq lanes: ${commandError.message}\n`);
286
287
  }
287
288
  printJson({ ...commandError.toJSON(), message: commandError.message });
288
- process.exit(1);
289
+ process.exit(["signal_not_found", "no_conductor_link"].includes(commandError.error) ? 2 : 1);
289
290
  }
290
291
  const message = commandError instanceof Error ? commandError.message : String(commandError);
291
292
  process.stderr.write(`hq lanes: ${message}${commandError instanceof Error && commandError.stack ? `\n${commandError.stack}` : ""}\n`);
@@ -1935,6 +1936,7 @@ async function refreshRemoteLaneMirrors(ctx, hqRoot, records, parentLaneIds, opt
1935
1936
  before.envelope_summary !== envelopeSummary ||
1936
1937
  (mirroredReportAt !== undefined && before.last_host_report_at !== mirroredReportAt) ||
1937
1938
  (isTerminalLaneState(owner.state) && (JSON.stringify(before.last_envelope) !== JSON.stringify(owner.last_envelope) ||
1939
+ JSON.stringify(before.outcome) !== JSON.stringify(owner.outcome) ||
1938
1940
  before.story_complete !== owner.story_complete ||
1939
1941
  before.story_complete_reason !== owner.story_complete_reason ||
1940
1942
  before.story_status !== owner.story_status ||
@@ -1964,6 +1966,7 @@ async function refreshRemoteLaneMirrors(ctx, hqRoot, records, parentLaneIds, opt
1964
1966
  ...(mirroredReportAt !== undefined ? { last_host_report_at: mirroredReportAt } : {}),
1965
1967
  ...(isTerminalLaneState(owner.state) ? {
1966
1968
  last_envelope: owner.last_envelope,
1969
+ outcome: owner.outcome,
1967
1970
  story_complete: owner.story_complete,
1968
1971
  story_complete_reason: owner.story_complete_reason,
1969
1972
  story_status: owner.story_status,
@@ -3465,7 +3468,34 @@ export function cancelPendingQuestionsForLane(hqRoot, laneId, nowIso, operatorRe
3465
3468
  function mintMessageId() {
3466
3469
  return `msg_${Date.now().toString(36)}_${randomBytes(3).toString("hex")}`;
3467
3470
  }
3471
+ /** In-process signal entry point sharing CLI admission, dedupe and conductor rules. */
3472
+ export async function createLaneFromSignal(input, deps = {}) {
3473
+ const ctx = resolveDeps({ ...deps, hqRoot: () => input.hqRoot });
3474
+ const company = await ctx.resolveCompany(input.company);
3475
+ const result = await createSignalLane(input.hqRoot, {
3476
+ company: company.slug, signal: input.signal_id, env: ctx.env,
3477
+ }, options => runCreate(ctx, { ...options, company: company.slug, project: "executive-bot",
3478
+ noStory: `signal:${input.signal_id}`, freshThread: true, quiet: true }));
3479
+ return { lane_id: result.lane.lane_id, existing: result.existing };
3480
+ }
3468
3481
  async function runCreate(ctx, opts) {
3482
+ if (opts.signal !== undefined) {
3483
+ if (!opts.company?.trim())
3484
+ throw new LanesError("missing_company", "--signal requires --company");
3485
+ if (opts.story || opts.additionalStory?.length || opts.briefFile || (opts.project && opts.project !== "executive-bot")) {
3486
+ throw new LanesError("signal_option_conflict", "Signal lanes use project executive-bot and a generated brief without a story");
3487
+ }
3488
+ const company = await ctx.resolveCompany(opts.company);
3489
+ const result = await createSignalLane(ctx.hqRoot(), {
3490
+ company: company.slug, signal: opts.signal, rulesFile: opts.rulesFile ? resolveCallerPath(ctx.callerCwd(), opts.rulesFile) : undefined,
3491
+ anyType: opts.anyType, senior: opts.senior, link: opts.link, env: ctx.env,
3492
+ }, input => runCreate(ctx, { ...opts, ...input, company: company.slug, signal: undefined, project: "executive-bot", noStory: `signal:${opts.signal}`, freshThread: opts.runner === "subagent" ? undefined : true, quiet: true }));
3493
+ if (!opts.quiet)
3494
+ printJson({ lane_id: result.lane.lane_id, signal_id: result.lane.signal.signal_id,
3495
+ source_ref: result.lane.signal.source_ref, senior: result.lane.senior, link: result.lane.signal_link ?? null,
3496
+ existing: result.existing, brief_path: result.brief_path });
3497
+ return result.lane;
3498
+ }
3469
3499
  if (opts.runner !== undefined && parseLaneRunner(opts.runner) === "subagent") {
3470
3500
  return runCreateSubagent(ctx, opts);
3471
3501
  }
@@ -4304,6 +4334,7 @@ async function runCreate(ctx, opts) {
4304
4334
  const admissionReservedAt = admission.action === "start" ? ctx.now().toISOString() : undefined;
4305
4335
  const laneInit = {
4306
4336
  company,
4337
+ ...(opts.signalMetadata ? { signal: opts.signalMetadata, signal_link: opts.signalLink } : {}),
4307
4338
  ...(company === null && meshCompany
4308
4339
  ? { mesh_company: meshCompany }
4309
4340
  : {}),
@@ -6182,7 +6213,54 @@ function formatLaneListHuman(lanes) {
6182
6213
  rows.push("No lanes.");
6183
6214
  return rows.join("\n");
6184
6215
  }
6185
- async function runStatus(ctx, opts) {
6216
+ async function runLaneStatus(ctx, laneId, opts) {
6217
+ const lane = (await resolveMessageLaneTarget(ctx, laneId)).lane;
6218
+ const status = {
6219
+ schema_version: "hq-lanes-lane-status.v1",
6220
+ lane_id: lane.lane_id,
6221
+ company: lane.company?.slug ?? null,
6222
+ state: lane.state,
6223
+ decision: lane.last_envelope?.decision ?? null,
6224
+ envelope_ref: lane.last_envelope?.ref ?? null,
6225
+ story_status: lane.story_status ?? null,
6226
+ story_complete: lane.story_complete ?? null,
6227
+ outcome: lane.outcome ?? null,
6228
+ };
6229
+ if (opts.json) {
6230
+ printJson(status);
6231
+ return;
6232
+ }
6233
+ const lines = [
6234
+ `lane ${status.lane_id}: ${status.state}` +
6235
+ (status.decision ? ` (decision ${status.decision})` : ""),
6236
+ ];
6237
+ if (status.outcome) {
6238
+ lines.push(`outcome ${status.outcome.state}: ${status.outcome.outcome}`);
6239
+ for (const item of status.outcome.gathered) {
6240
+ lines.push(` gathered ${item.label}: ${item.text}${item.ref ? ` (${item.ref})` : ""}`);
6241
+ }
6242
+ for (const step of status.outcome.next_steps)
6243
+ lines.push(` next: ${step}`);
6244
+ if (status.outcome.decision_id)
6245
+ lines.push(` decision_id: ${status.outcome.decision_id}`);
6246
+ }
6247
+ else {
6248
+ lines.push("outcome: none");
6249
+ }
6250
+ console.log(lines.join("\n"));
6251
+ }
6252
+ async function runStatus(ctx, opts, laneId) {
6253
+ if (laneId !== undefined) {
6254
+ if (opts.project !== undefined || opts.company !== undefined) {
6255
+ throw new LanesError("invalid_args", "hq lanes status <lane-id> reports one lane; drop --project and --company", { lane_id: laneId });
6256
+ }
6257
+ await runLaneStatus(ctx, laneId, opts);
6258
+ return;
6259
+ }
6260
+ if (opts.project === undefined) {
6261
+ throw new LanesError("invalid_args", "hq lanes status requires --project <slug>, or a lane id for one lane", {});
6262
+ }
6263
+ const projectId = opts.project;
6186
6264
  if (!ctx.storyLedgerEnabled()) {
6187
6265
  throw new LanesError("story_ledger_disabled", "hq lanes status requires the lanes.story-ledger flag", {
6188
6266
  flag: "lanes.story-ledger",
@@ -6216,7 +6294,7 @@ async function runStatus(ctx, opts) {
6216
6294
  }
6217
6295
  const snapshot = buildLaneStatusSnapshot({
6218
6296
  hqRoot,
6219
- projectId: opts.project,
6297
+ projectId,
6220
6298
  ...(company ? { companySlug: company.slug } : {}),
6221
6299
  ...(company ? { companyUid: company.uid } : {}),
6222
6300
  lanes,
@@ -6500,6 +6578,7 @@ async function runShow(ctx, laneId, hasPendingQuestionForLane, resolveLaneTarget
6500
6578
  fingerprint: lane.fingerprint ?? null,
6501
6579
  envelope_repair_mode: lane.envelope_repair?.mode ?? null,
6502
6580
  envelope_validation_errors: lane.envelope_validation_errors ?? [],
6581
+ outcome: lane.outcome ?? null,
6503
6582
  last_resume: lane.last_resume ?? null,
6504
6583
  // Public JSON key for the bounded start audit; `last_resume` remains unchanged.
6505
6584
  resume_history: lane.resume_history ?? [],
@@ -9162,6 +9241,7 @@ async function runCreateSubagent(ctx, opts) {
9162
9241
  const createdAt = ctx.now().toISOString();
9163
9242
  const lane = createLane(hqRoot, {
9164
9243
  company,
9244
+ ...(opts.signalMetadata ? { signal: opts.signalMetadata, signal_link: opts.signalLink } : {}),
9165
9245
  ...(company === null && meshCompany ? { mesh_company: meshCompany } : {}),
9166
9246
  project_id: project,
9167
9247
  story_id: story ?? "",
@@ -10402,6 +10482,7 @@ export function registerLanesCommand(parent, deps = {}) {
10402
10482
  });
10403
10483
  registerLaneReportCommands(lanes, {
10404
10484
  hqRoot: ctx.hqRoot,
10485
+ laneRunsHere: (lane) => laneRunsHere(lane, reconcileHostFromCtx(ctx)),
10405
10486
  resolveCompany: ctx.resolveCompany,
10406
10487
  now: ctx.now,
10407
10488
  focusDashboardEnabled: (company) => readLanesRegistryFlagFailOff(LANE_FOCUS_DASHBOARD_FLAG, {
@@ -10446,12 +10527,16 @@ export function registerLanesCommand(parent, deps = {}) {
10446
10527
  .description("Register a lane, run admission, and return immediately")
10447
10528
  .option("--company <slug|uid>", "Company slug or cloud uid; omit for a core/personal lane")
10448
10529
  .option("--mesh-company <slug|uid>", "Explicit Work Mesh company for a personal lane; membership is verified")
10449
- .requiredOption("--project <slug>", "HQ project slug")
10530
+ .option("--project <slug>", "HQ project slug (executive-bot for --signal)")
10531
+ .option("--signal <id>", "Create one lane for a company signal")
10532
+ .option("--rules-file <path>", "Standing rules for a signal lane")
10533
+ .option("--any-type", "Allow signal types other than action_item")
10534
+ .option("--link <id>", "Conductor link for a signal lane")
10450
10535
  .option("--story <id>", "Story id from the project's prd.json")
10451
10536
  .option("--focus-override <reason>", "Human-approved reason to override the lane focus guard")
10452
10537
  .addOption(noStoryOption)
10453
10538
  .option("--additional-story <id>", "Additional story carried by this lane; repeatable and discouraged when active", collectRepeatable)
10454
- .requiredOption("--brief-file <path>", "Path to the lane brief")
10539
+ .option("--brief-file <path>", "Path to the lane brief (generated for --signal)")
10455
10540
  .option("--worker <name>", "HQ worker or reusable profile id")
10456
10541
  .option("--fresh-thread", "Skip worker continuity and start a cold provider session")
10457
10542
  .option("--loop", "Keep one worker lane registered to process queued phase envelopes")
@@ -10697,12 +10782,13 @@ export function registerLanesCommand(parent, deps = {}) {
10697
10782
  }));
10698
10783
  lanes
10699
10784
  .command("status")
10700
- .description("Summarize project stories, lanes, PRs, blockers, and pending questions")
10701
- .requiredOption("--project <slug>", "HQ project slug")
10785
+ .description("Summarize project stories, lanes, PRs, blockers, and pending questions; with a lane id, report that lane's outcome")
10786
+ .argument("[lane-id]", "Report one lane's state and outcome block instead of a project")
10787
+ .option("--project <slug>", "HQ project slug (required without a lane id)")
10702
10788
  .option("--company <slug|uid>", "Company project scope; omit for a personal project")
10703
10789
  .option("--json", "Emit JSON on stdout")
10704
- .action(wrap(async (opts) => {
10705
- await runStatus(ctx, opts);
10790
+ .action(wrap(async (laneId, opts) => {
10791
+ await runStatus(ctx, opts, laneId);
10706
10792
  }));
10707
10793
  lanes
10708
10794
  .command("capacity")
@@ -6,6 +6,7 @@ import { sessionIdFromEnv } from "../lib/core-utils/session-id.js";
6
6
  import { vaultApiFetch, getCompanyUid, resolveCallerPersonUid, } from "../utils/vault-api.js";
7
7
  import { isPlanGateError, planGateErrorFromPayload, } from "../utils/plan-gate-error.js";
8
8
  import { mapWithConcurrency } from "./sync-manifest.js";
9
+ import { fetchAttendedLiveBots } from "../lib/live-meetings.js";
9
10
  import { isUsageOnly, pricingPlanFromBody, resolveCompanyPricingPlan, usageOnlyCardRequiredLines, usagePauseFromBody, } from "../lib/billing/usage-only.js";
10
11
  const LIVE_SNAPSHOT_STATUSES = new Set([
11
12
  "joining",
@@ -110,27 +111,6 @@ function liveBotTime(bot) {
110
111
  .filter(Number.isFinite);
111
112
  return times.length > 0 ? Math.max(...times) : Number.NEGATIVE_INFINITY;
112
113
  }
113
- /**
114
- * Live meetings in this company the caller may read but did not record.
115
- *
116
- * `/v1/bot/list` returns only the caller's own notetakers, so a meeting a
117
- * coworker recorded never appears there. The meetings API lists the live
118
- * meetings the caller is an attendee of. An older server ignores `view=live`
119
- * and answers with the ordinary meeting list, which has no `bots`, and any
120
- * failure here leaves the caller's own list unchanged.
121
- */
122
- async function fetchAttendedLiveBots(token, companyId) {
123
- try {
124
- const res = await vaultApiFetch({ token, path: "/v1/meetings", query: { companyId, view: "live" } });
125
- if (!res.ok)
126
- return [];
127
- const data = (await res.json());
128
- return Array.isArray(data.bots) ? data.bots : [];
129
- }
130
- catch {
131
- return [];
132
- }
133
- }
134
114
  /** The caller's own bots first; attended meetings they did not record added after. */
135
115
  function mergeLiveMeetingBots(own, attended) {
136
116
  const seen = new Set(own.map(liveBotId).filter((id) => Boolean(id)));
@@ -11,6 +11,8 @@ import { Command } from "commander";
11
11
  import { type RunSignalsResult, type SignalsStepChoice, type SignalsTrigger } from "../lib/signals/engine.js";
12
12
  import type { SignalsRunAlertSenders } from "../lib/signals/failure-alert.js";
13
13
  interface RunCliOptions {
14
+ source?: string;
15
+ incremental?: boolean;
14
16
  company?: string;
15
17
  dryRun?: boolean;
16
18
  step: SignalsStepChoice;
@@ -109,6 +109,8 @@ export async function runSignalsCli(options, overrides = {}) {
109
109
  const result = await runSignals({
110
110
  hqRoot: options.hqRoot,
111
111
  company: options.company,
112
+ source: options.source,
113
+ incremental: options.incremental,
112
114
  dryRun: options.dryRun === true,
113
115
  step: options.step,
114
116
  trigger: options.trigger,
@@ -134,7 +136,8 @@ export async function runSignalsCli(options, overrides = {}) {
134
136
  process.stdout.write(`${JSON.stringify({ status, startedAt, trigger })}\n`);
135
137
  }
136
138
  else if (options.json) {
137
- const payload = options.company && result.records.length === 1 ? result.records[0] : result;
139
+ const record = result.records[0];
140
+ const payload = options.incremental && record ? { ...record, ...record.steps.find((s) => s.name === "extract")?.detail } : options.company && result.records.length === 1 ? record : result;
138
141
  process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
139
142
  }
140
143
  else {
@@ -150,6 +153,8 @@ export function registerSignalsRunSubcommand(signals) {
150
153
  run
151
154
  .description("Run the local signals engine (garden, adapters, extract, garden, upload) for companies with capture on")
152
155
  .option("--company <slug>", "Run one company (default: every company listed in the companies manifest)")
156
+ .option("--source <ref>", "Read a growing source: meeting:<id> or <channel>:<file> (needs --incremental and --company)")
157
+ .option("--incremental", "Extract only new signals from --source, retaining a cursor and prior signal ids")
153
158
  .option("--dry-run", "Print what would run; run no mutating step and write nothing")
154
159
  .addOption(new Option("--step <step>", "Run one step").choices([...SIGNALS_STEP_CHOICES]).default("all"))
155
160
  .addOption(new Option("--trigger <trigger>", "What started the run").choices([...SIGNALS_TRIGGERS]).default("manual"))
@@ -187,6 +187,15 @@ async function main() {
187
187
  for await (const chunk of process.stdin)
188
188
  chunks.push(Buffer.from(chunk));
189
189
  const text = Buffer.concat(chunks).toString("utf8");
190
+ if (process.env.HQ_LANE_SIGNAL_LINK && process.env.HQ_ROOT) {
191
+ try {
192
+ const { bindSignalLaneHook } = await import("./lib/lanes/links.js");
193
+ bindSignalLaneHook(process.env.HQ_ROOT, text, process.env);
194
+ }
195
+ catch {
196
+ process.stderr.write("hq-lanes-drain: signal_link_binding_failed; conductor link remains unavailable\n");
197
+ }
198
+ }
190
199
  let input = {};
191
200
  let inputError = "";
192
201
  try {
@@ -62,6 +62,11 @@ export interface JobRunCompletion {
62
62
  }
63
63
  /** A job record as the cloud returns it (hq-pro-agents jobs/schema.ts `JobRecord`). */
64
64
  export interface BotJobRecord {
65
+ trigger?: {
66
+ kind: "event";
67
+ event: string;
68
+ filter?: Record<string, string>;
69
+ };
65
70
  jobId: string;
66
71
  prompt: string;
67
72
  status?: string;
@@ -0,0 +1,31 @@
1
+ export declare const CLAUDE_KEYCHAIN_SERVICE = "Claude Code-credentials";
2
+ export declare const CLAUDE_LOGIN_ENV_VARS: readonly ["ANTHROPIC_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN", "ANTHROPIC_API_KEY"];
3
+ export type ClaudeLoginStatus = "ok" | "missing" | "expired" | "skipped";
4
+ export interface ClaudeLoginSource {
5
+ source: string;
6
+ status: ClaudeLoginStatus;
7
+ reason: string;
8
+ }
9
+ export interface ClaudeLoginCheck {
10
+ signedIn: boolean;
11
+ /** The first source the runtime would use, when signed in. */
12
+ via: string | null;
13
+ sources: ClaudeLoginSource[];
14
+ }
15
+ export interface KeychainRead {
16
+ code: number;
17
+ stdout: string;
18
+ stderr: string;
19
+ }
20
+ export interface ClaudeLoginDeps {
21
+ env?: NodeJS.ProcessEnv;
22
+ home?: string;
23
+ platform?: NodeJS.Platform;
24
+ readKeychain?: () => KeychainRead;
25
+ now?: () => number;
26
+ }
27
+ export declare function checkClaudeCodeLogin(deps?: ClaudeLoginDeps): ClaudeLoginCheck;
28
+ /** One sentence listing every source that did not yield a login. */
29
+ export declare function describeClaudeLoginSources(check: ClaudeLoginCheck): string;
30
+ export declare function claudeLoginFix(botName?: string): string;
31
+ //# sourceMappingURL=claude-login.d.ts.map