@indigoai-us/hq-cli 5.345.39 → 5.345.41
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 +8 -0
- package/assets/scaffold/core/scripts/derive-trigger-facts.sh +12 -3
- package/assets/scaffold/core/scripts/rebuild-companies-index.sh +12 -0
- package/dist/lib/doctor/checks/stranded-lanes.d.ts +7 -0
- package/dist/lib/doctor/checks/stranded-lanes.js +45 -0
- package/dist/lib/doctor/client-health-reason-codes-flag.d.ts +8 -0
- package/dist/lib/doctor/client-health-reason-codes-flag.js +22 -0
- package/dist/lib/doctor/fix/apply.js +36 -0
- package/dist/lib/doctor/registry.js +4 -0
- package/dist/lib/flag-registry.js +10 -0
- package/dist/lib/lanes/keep-alive-audit.d.ts +5 -2
- package/dist/lib/lanes/keep-alive-audit.js +53 -3
- package/dist/lib/lanes/spawn.js +1 -1
- package/dist/sentry-init.d.ts +1 -0
- package/dist/sentry-init.js +106 -3
- package/dist/utils/resident-package-preload.js +51 -17
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.345.41] — 2026-10-07
|
|
6
|
+
- Short-lived hq commands now use a fresh cached company flag snapshot while the registry refresh is pending.
|
|
7
|
+
- Report failed and quota-interrupted keep-alive lead lanes as stranded in the keep-alive audit and `hq doctor`, with the senior recovery command and quota reset time.
|
|
8
|
+
|
|
9
|
+
## [5.345.40] — 2026-10-07
|
|
10
|
+
- Fix published shell parity for rebuild-index company help and Git Bash trigger-facts stdin.
|
|
11
|
+
- Add separate default-off gates: `client-health-show-manual-only` controls CLI remediation output, while `client-health-skip-manual-only-reports` suppresses other manual-only reports and writes a local family diagnostic; `sync.manifest` and `sync.runtime` reports remain filed, and failed automatic fixes remain reportable.
|
|
12
|
+
|
|
5
13
|
## [5.345.39] — 2026-10-07
|
|
6
14
|
- Cap each `hq doctor --fix` invocation at two company journal refreshes and persist a rotating cursor so pending journals continue on later runs.
|
|
7
15
|
- Add `hq db destroy --company <slug> --app <app>` to permanently delete an app's remote database. It requires typing the app id (or `--confirm <app>` when not interactive), offers `hq db dump` first (`--dump-first` / `--no-dump`), and only company owners and admins can run it.
|
|
@@ -55,9 +55,18 @@ EVENT="${1:-}"
|
|
|
55
55
|
WITH_ASSISTANT_INTENT=0
|
|
56
56
|
[ "${2:-}" = "--with-assistant-intent" ] && WITH_ASSISTANT_INTENT=1
|
|
57
57
|
|
|
58
|
-
# Bash
|
|
59
|
-
#
|
|
60
|
-
|
|
58
|
+
# Git Bash may not expose /dev/stdin. Use Bash's builtin there so the hook path
|
|
59
|
+
# does not fork; keep the original command-substitution behavior on POSIX.
|
|
60
|
+
case "${OSTYPE:-}" in
|
|
61
|
+
msys*|cygwin*)
|
|
62
|
+
STDIN_JSON=''
|
|
63
|
+
IFS= read -r -d '' STDIN_JSON 2>/dev/null || true
|
|
64
|
+
while [[ "$STDIN_JSON" == *$'\n' ]]; do
|
|
65
|
+
STDIN_JSON="${STDIN_JSON%$'\n'}"
|
|
66
|
+
done
|
|
67
|
+
;;
|
|
68
|
+
*) STDIN_JSON="$(</dev/stdin)" || STDIN_JSON='{}' ;;
|
|
69
|
+
esac
|
|
61
70
|
[ -n "$STDIN_JSON" ] || STDIN_JSON='{}'
|
|
62
71
|
JQ="$(command -v jq || true)"
|
|
63
72
|
|
|
@@ -13,6 +13,18 @@
|
|
|
13
13
|
|
|
14
14
|
set -euo pipefail
|
|
15
15
|
|
|
16
|
+
for arg in "$@"; do
|
|
17
|
+
if [[ "$arg" == "--help" || "$arg" == "-h" ]]; then
|
|
18
|
+
printf '%s\n' \
|
|
19
|
+
'Usage: hq core rebuild-index companies [--company <slug>]' \
|
|
20
|
+
'' \
|
|
21
|
+
'Regenerate company root INDEX.md files. Without --company, all companies are regenerated.' \
|
|
22
|
+
'--company <slug> regenerates only that company'\''s index.' \
|
|
23
|
+
'Company-scoped writes require the cli.rebuild-index-company-scope flag.'
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
done
|
|
27
|
+
|
|
16
28
|
HQ_ROOT="${HQ_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
|
|
17
29
|
cd "$HQ_ROOT"
|
|
18
30
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Doctor alarm for failed lead lanes that are configured to keep running. */
|
|
2
|
+
import type { CheckContext, CheckFamily, CheckResult } from "../types.js";
|
|
3
|
+
export declare const STRANDED_LANES_FAMILY_ID = "stranded-lanes";
|
|
4
|
+
export declare const STRANDED_LANES_FAMILY_TITLE = "Stranded lanes";
|
|
5
|
+
export declare const strandedLanesFamily: CheckFamily;
|
|
6
|
+
export declare function checkStrandedLanes(context: CheckContext): CheckResult[];
|
|
7
|
+
//# sourceMappingURL=stranded-lanes.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Doctor alarm for failed lead lanes that are configured to keep running. */
|
|
2
|
+
import { readAllLanes } from "../../lanes/store.js";
|
|
3
|
+
export const STRANDED_LANES_FAMILY_ID = "stranded-lanes";
|
|
4
|
+
export const STRANDED_LANES_FAMILY_TITLE = "Stranded lanes";
|
|
5
|
+
export const strandedLanesFamily = {
|
|
6
|
+
id: STRANDED_LANES_FAMILY_ID,
|
|
7
|
+
title: STRANDED_LANES_FAMILY_TITLE,
|
|
8
|
+
run: (context) => Promise.resolve(checkStrandedLanes(context)),
|
|
9
|
+
};
|
|
10
|
+
export function checkStrandedLanes(context) {
|
|
11
|
+
let stranded;
|
|
12
|
+
try {
|
|
13
|
+
stranded = readAllLanes(context.hqRoot).filter((lane) => lane.depth === 0 && lane.keep_alive === true && (lane.state === "failed" ||
|
|
14
|
+
(lane.state === "interrupted" && lane.terminal_reason === "account_quota_exhausted")));
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
return [{
|
|
18
|
+
status: "UNKNOWN",
|
|
19
|
+
checkId: "stranded-lanes.read-error",
|
|
20
|
+
message: `Could not inspect failed keep-alive lead lanes: ${error instanceof Error ? error.message : String(error)}`,
|
|
21
|
+
}];
|
|
22
|
+
}
|
|
23
|
+
if (stranded.length === 0) {
|
|
24
|
+
return [{
|
|
25
|
+
status: "PASS",
|
|
26
|
+
checkId: "stranded-lanes.none",
|
|
27
|
+
message: "No failed keep-alive lead lanes.",
|
|
28
|
+
}];
|
|
29
|
+
}
|
|
30
|
+
return stranded.map((lane) => {
|
|
31
|
+
const quotaExhausted = lane.terminal_reason === "account_quota_exhausted";
|
|
32
|
+
const state = lane.state;
|
|
33
|
+
return {
|
|
34
|
+
status: "FAIL",
|
|
35
|
+
checkId: `stranded-lanes.${lane.lane_id}`,
|
|
36
|
+
details: {
|
|
37
|
+
lane_id: lane.lane_id,
|
|
38
|
+
...(lane.terminal_reason ? { terminal_reason: lane.terminal_reason } : {}),
|
|
39
|
+
},
|
|
40
|
+
message: `Keep-alive lead lane ${lane.lane_id} is ${state}${quotaExhausted ? " after account quota exhaustion" : ""}.`,
|
|
41
|
+
remediation: `Ask the senior to recover it with: hq lanes resume ${lane.lane_id} --from-failed --reason "recover stranded keep-alive lane"`,
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=stranded-lanes.js.map
|
|
@@ -4,6 +4,10 @@ export declare const CLIENT_HEALTH_REASON_CODES_FLAG_FALLBACK = false;
|
|
|
4
4
|
export declare const CLIENT_HEALTH_REASON_CODES_FLAG_TIMEOUT_MS = 150;
|
|
5
5
|
export declare const CLIENT_HEALTH_CHECK_STATUS_FLAG = "client-health-sync-check-status";
|
|
6
6
|
export declare const CLIENT_HEALTH_CHECK_STATUS_FLAG_FALLBACK = false;
|
|
7
|
+
export declare const CLIENT_HEALTH_SHOW_MANUAL_ONLY_FLAG = "client-health-show-manual-only";
|
|
8
|
+
export declare const CLIENT_HEALTH_SHOW_MANUAL_ONLY_FLAG_FALLBACK = false;
|
|
9
|
+
export declare const CLIENT_HEALTH_SKIP_MANUAL_ONLY_REPORTS_FLAG = "client-health-skip-manual-only-reports";
|
|
10
|
+
export declare const CLIENT_HEALTH_SKIP_MANUAL_ONLY_REPORTS_FLAG_FALLBACK = false;
|
|
7
11
|
type ClientHealthFlagClient = Pick<FlagClient, "ready" | "isEnabled" | "close">;
|
|
8
12
|
export interface ClientHealthReasonCodesFlagDependencies {
|
|
9
13
|
env?: NodeJS.ProcessEnv;
|
|
@@ -25,6 +29,10 @@ export interface ClientHealthReasonCodesCompany {
|
|
|
25
29
|
export declare function clientHealthReasonCodesEnabled(dependencies?: ClientHealthReasonCodesFlagDependencies, callerSignal?: AbortSignal): Promise<boolean>;
|
|
26
30
|
/** Resolve the separate status-report gate for the active company. */
|
|
27
31
|
export declare function clientHealthCheckStatusEnabled(dependencies?: ClientHealthReasonCodesFlagDependencies, callerSignal?: AbortSignal): Promise<boolean>;
|
|
32
|
+
/** Resolve the default-off gate for showing manual-only doctor output. */
|
|
33
|
+
export declare function clientHealthShowManualOnlyEnabled(dependencies?: ClientHealthReasonCodesFlagDependencies, callerSignal?: AbortSignal): Promise<boolean>;
|
|
34
|
+
/** Resolve the separate default-off gate for skipping manual-only reports. */
|
|
35
|
+
export declare function clientHealthSkipManualOnlyReportsEnabled(dependencies?: ClientHealthReasonCodesFlagDependencies, callerSignal?: AbortSignal): Promise<boolean>;
|
|
28
36
|
/** Resolve the same default-off gate for one explicitly identified company. */
|
|
29
37
|
export declare function clientHealthReasonCodesEnabledForCompany(company: ClientHealthReasonCodesCompany, dependencies?: ClientHealthReasonCodesFlagDependencies, callerSignal?: AbortSignal): Promise<boolean>;
|
|
30
38
|
/** Resolve the separate default-off check-status gate for one company. */
|
|
@@ -6,6 +6,10 @@ export const CLIENT_HEALTH_REASON_CODES_FLAG_FALLBACK = false;
|
|
|
6
6
|
export const CLIENT_HEALTH_REASON_CODES_FLAG_TIMEOUT_MS = 150;
|
|
7
7
|
export const CLIENT_HEALTH_CHECK_STATUS_FLAG = "client-health-sync-check-status";
|
|
8
8
|
export const CLIENT_HEALTH_CHECK_STATUS_FLAG_FALLBACK = false;
|
|
9
|
+
export const CLIENT_HEALTH_SHOW_MANUAL_ONLY_FLAG = "client-health-show-manual-only";
|
|
10
|
+
export const CLIENT_HEALTH_SHOW_MANUAL_ONLY_FLAG_FALLBACK = false;
|
|
11
|
+
export const CLIENT_HEALTH_SKIP_MANUAL_ONLY_REPORTS_FLAG = "client-health-skip-manual-only-reports";
|
|
12
|
+
export const CLIENT_HEALTH_SKIP_MANUAL_ONLY_REPORTS_FLAG_FALLBACK = false;
|
|
9
13
|
function safeErrorClass(error) {
|
|
10
14
|
const name = error instanceof Error ? error.name : "UnknownError";
|
|
11
15
|
return /^[A-Za-z][A-Za-z0-9]{0,63}$/.test(name) ? name : "UnknownError";
|
|
@@ -44,6 +48,24 @@ export async function clientHealthCheckStatusEnabled(dependencies = {}, callerSi
|
|
|
44
48
|
companySlug: env.HQ_COMPANY_SLUG?.trim() || undefined,
|
|
45
49
|
}, dependencies, callerSignal);
|
|
46
50
|
}
|
|
51
|
+
/** Resolve the default-off gate for showing manual-only doctor output. */
|
|
52
|
+
export async function clientHealthShowManualOnlyEnabled(dependencies = {}, callerSignal) {
|
|
53
|
+
const env = dependencies.env ?? process.env;
|
|
54
|
+
const companyUid = env.HQ_COMPANY_UID?.trim() || "";
|
|
55
|
+
if (!/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid)) {
|
|
56
|
+
return CLIENT_HEALTH_SHOW_MANUAL_ONLY_FLAG_FALLBACK;
|
|
57
|
+
}
|
|
58
|
+
return clientHealthFlagEnabledForCompany(CLIENT_HEALTH_SHOW_MANUAL_ONLY_FLAG, CLIENT_HEALTH_SHOW_MANUAL_ONLY_FLAG_FALLBACK, { companyUid, companySlug: env.HQ_COMPANY_SLUG?.trim() || undefined }, dependencies, callerSignal);
|
|
59
|
+
}
|
|
60
|
+
/** Resolve the separate default-off gate for skipping manual-only reports. */
|
|
61
|
+
export async function clientHealthSkipManualOnlyReportsEnabled(dependencies = {}, callerSignal) {
|
|
62
|
+
const env = dependencies.env ?? process.env;
|
|
63
|
+
const companyUid = env.HQ_COMPANY_UID?.trim() || "";
|
|
64
|
+
if (!/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid)) {
|
|
65
|
+
return CLIENT_HEALTH_SKIP_MANUAL_ONLY_REPORTS_FLAG_FALLBACK;
|
|
66
|
+
}
|
|
67
|
+
return clientHealthFlagEnabledForCompany(CLIENT_HEALTH_SKIP_MANUAL_ONLY_REPORTS_FLAG, CLIENT_HEALTH_SKIP_MANUAL_ONLY_REPORTS_FLAG_FALLBACK, { companyUid, companySlug: env.HQ_COMPANY_SLUG?.trim() || undefined }, dependencies, callerSignal);
|
|
68
|
+
}
|
|
47
69
|
/** Resolve the same default-off gate for one explicitly identified company. */
|
|
48
70
|
export async function clientHealthReasonCodesEnabledForCompany(company, dependencies = {}, callerSignal) {
|
|
49
71
|
return clientHealthFlagEnabledForCompany(CLIENT_HEALTH_REASON_CODES_FLAG, CLIENT_HEALTH_REASON_CODES_FLAG_FALLBACK, company, dependencies, callerSignal);
|
|
@@ -41,6 +41,7 @@ import { botDir } from "../../bot/paths.js";
|
|
|
41
41
|
import { readBotConfig } from "../../bot/config.js";
|
|
42
42
|
import { buildBotDaemonPaths, rewriteBotDaemonNodePath } from "../../bot/daemon.js";
|
|
43
43
|
import { classifyRefreshSyncOutcome, } from "./refresh-sync-outcome.js";
|
|
44
|
+
import { clientHealthShowManualOnlyEnabled } from "../client-health-reason-codes-flag.js";
|
|
44
45
|
/** The tree subtrees whose uncommitted changes block a `--fix` run. */
|
|
45
46
|
export const HOOK_CONFIG_DIRS = [".claude", ".codex", ".grok"];
|
|
46
47
|
/** Per-company bound for the default `hq sync pull` repair. */
|
|
@@ -117,6 +118,17 @@ export async function applyFixes(options) {
|
|
|
117
118
|
return empty({ exitCode: 1, refused: "dirty-tree" });
|
|
118
119
|
}
|
|
119
120
|
}
|
|
121
|
+
// Resolve the customer-visible manual-only output gate once per fix run.
|
|
122
|
+
// Keep it behind the dirty-tree guard, and fail open to the pre-change
|
|
123
|
+
// output if the identity is absent or the registry lookup fails.
|
|
124
|
+
let showManualOnlyRemediations = false;
|
|
125
|
+
try {
|
|
126
|
+
showManualOnlyRemediations =
|
|
127
|
+
await clientHealthShowManualOnlyEnabled();
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
writeErr("HQ client-health manual-only flag lookup failed; keeping manual-only instructions hidden.\n");
|
|
131
|
+
}
|
|
120
132
|
const collect = options.runChecks ?? defaultRunChecks;
|
|
121
133
|
const refreshSync = options.refreshSync ?? defaultRefreshSync;
|
|
122
134
|
const rewriteBotNodePath = options.rewriteBotNodePath ?? defaultRewriteBotNodePath;
|
|
@@ -174,6 +186,8 @@ export async function applyFixes(options) {
|
|
|
174
186
|
refused = "declined";
|
|
175
187
|
if (planned.length === 0) {
|
|
176
188
|
write("Aborted — no changes written.\n");
|
|
189
|
+
if (showManualOnlyRemediations)
|
|
190
|
+
writeManualOnlyRemediations(findings, write);
|
|
177
191
|
return empty({ refused, fixableCount });
|
|
178
192
|
}
|
|
179
193
|
write(`Pass ${pass} declined — no further changes written.\n`);
|
|
@@ -214,6 +228,8 @@ export async function applyFixes(options) {
|
|
|
214
228
|
}
|
|
215
229
|
if (planned.length === 0) {
|
|
216
230
|
write("hq doctor --fix: nothing to repair — no auto-fixable findings.\n");
|
|
231
|
+
if (showManualOnlyRemediations)
|
|
232
|
+
writeManualOnlyRemediations(findings, write);
|
|
217
233
|
return empty({ refused, fixableCount });
|
|
218
234
|
}
|
|
219
235
|
// Show each repair's final post-fix status, including work from earlier passes.
|
|
@@ -243,6 +259,8 @@ export async function applyFixes(options) {
|
|
|
243
259
|
if (planned.at(-1)?.pass === MAX_FIX_PASSES && buildPlans(findings).length > 0) {
|
|
244
260
|
write(`Stopped after ${MAX_FIX_PASSES} repair passes; new auto-fixable findings remain.\n`);
|
|
245
261
|
}
|
|
262
|
+
if (showManualOnlyRemediations)
|
|
263
|
+
writeManualOnlyRemediations(findings, write);
|
|
246
264
|
return {
|
|
247
265
|
exitCode: 0,
|
|
248
266
|
wrote: true,
|
|
@@ -254,6 +272,24 @@ export async function applyFixes(options) {
|
|
|
254
272
|
applied,
|
|
255
273
|
};
|
|
256
274
|
}
|
|
275
|
+
/** Print the available user action for findings that `--fix` deliberately skips. */
|
|
276
|
+
function writeManualOnlyRemediations(findings, write) {
|
|
277
|
+
const manual = findings.flatMap((result) => {
|
|
278
|
+
const remediation = deriveRemediation(result);
|
|
279
|
+
return remediation && !remediation.autoFixable
|
|
280
|
+
? [{ result, remediation }]
|
|
281
|
+
: [];
|
|
282
|
+
});
|
|
283
|
+
if (manual.length === 0)
|
|
284
|
+
return;
|
|
285
|
+
write("\nManual-only findings — complete these actions yourself:\n");
|
|
286
|
+
for (const { result, remediation } of manual) {
|
|
287
|
+
write(` ${result.checkId}: ${remediation.action}\n`);
|
|
288
|
+
if (remediation.command.trim()) {
|
|
289
|
+
write(` Command: ${remediation.command}\n`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
257
293
|
/** Run the default (read-only) check registry and flatten to a result list. */
|
|
258
294
|
async function defaultRunChecks(hqRoot) {
|
|
259
295
|
// `--fix` never repairs the integrations family (networked inventory), so
|
|
@@ -25,6 +25,7 @@ import { workContextFamily } from "./checks/work-context.js";
|
|
|
25
25
|
import { botsFamily } from "./checks/bots.js";
|
|
26
26
|
import { anywhereFamily } from "./checks/anywhere.js";
|
|
27
27
|
import { scaffoldCliFloorFamily } from "./checks/scaffold-cli-floor.js";
|
|
28
|
+
import { strandedLanesFamily } from "./checks/stranded-lanes.js";
|
|
28
29
|
import { fixtureCoverageFamily } from "./fixtures/discover.js";
|
|
29
30
|
import { checkClaudeWiring } from "./checks/claude-wiring.js";
|
|
30
31
|
/**
|
|
@@ -266,6 +267,9 @@ export function createDefaultRegistry() {
|
|
|
266
267
|
// HQ anywhere (US-012): pointer, registry, hqd service/socket/version,
|
|
267
268
|
// heartbeat, MCP registrations and skill links. NA when not installed.
|
|
268
269
|
registry.register(anywhereFamily);
|
|
270
|
+
// Failed keep-alive lead lanes cannot recover themselves; make them visible
|
|
271
|
+
// in the standard local doctor report with the senior recovery command.
|
|
272
|
+
registry.register(strandedLanesFamily);
|
|
269
273
|
return registry;
|
|
270
274
|
}
|
|
271
275
|
//# sourceMappingURL=registry.js.map
|
|
@@ -142,6 +142,16 @@ export function kickFlagRegistryReadiness(dependencies = {}) {
|
|
|
142
142
|
const sentry = dependencies.sentry;
|
|
143
143
|
const companyUid = company.companyUid;
|
|
144
144
|
const companyIdentifiers = [companyUid, company.companySlug].filter((value) => Boolean(value));
|
|
145
|
+
// Short-lived commands can reach a synchronous gate before the background
|
|
146
|
+
// worker receives the registry response. Hydrate only the validated,
|
|
147
|
+
// company-scoped persisted snapshot so those gates can use the last fresh
|
|
148
|
+
// decision immediately; the worker refresh below replaces it when available.
|
|
149
|
+
const persistedSnapshot = companyUid
|
|
150
|
+
? readFlagSnapshotFile(companyUid, { env: companyEnv })
|
|
151
|
+
: null;
|
|
152
|
+
if (persistedSnapshot) {
|
|
153
|
+
heldSnapshot = { version: 0, flags: persistedSnapshot.flags };
|
|
154
|
+
}
|
|
145
155
|
refreshCompletion = Promise.resolve();
|
|
146
156
|
completeRefresh = null;
|
|
147
157
|
refreshWorker = null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type KeepAliveAuditVerdict = "on_time" | "late" | "early_wake" | "message_wake" | "work_triggered" | "self_wake" | "early_by_keep_alive" | "restarted_untimed" | "not_restarted" | "keep_alive_off" | "waiting";
|
|
1
|
+
type KeepAliveAuditVerdict = "on_time" | "late" | "early_wake" | "message_wake" | "work_triggered" | "self_wake" | "early_by_keep_alive" | "restarted_untimed" | "not_restarted" | "stranded" | "keep_alive_off" | "waiting";
|
|
2
2
|
interface KeepAliveAuditRestart {
|
|
3
3
|
at: string | null;
|
|
4
4
|
trigger: string;
|
|
@@ -12,15 +12,18 @@ interface KeepAliveAuditRow {
|
|
|
12
12
|
stopped_at: string;
|
|
13
13
|
next_check_at: string | null;
|
|
14
14
|
due_at: string;
|
|
15
|
-
due_reason: "next_check_at" | "backoff";
|
|
15
|
+
due_reason: "next_check_at" | "backoff" | "quota_reset_at";
|
|
16
16
|
restart: KeepAliveAuditRestart | null;
|
|
17
17
|
blocked_outcome?: "woken" | "skipped";
|
|
18
18
|
blocked_outcome_reason?: string;
|
|
19
19
|
verdict: KeepAliveAuditVerdict;
|
|
20
|
+
remediation?: string;
|
|
21
|
+
terminal_reason?: string;
|
|
20
22
|
}
|
|
21
23
|
interface KeepAliveAuditSummary {
|
|
22
24
|
by_verdict: Record<KeepAliveAuditVerdict, number>;
|
|
23
25
|
not_restarted: number;
|
|
26
|
+
stranded: number;
|
|
24
27
|
restarted_untimed: number;
|
|
25
28
|
late_restarts_after_window: number;
|
|
26
29
|
late_over_10m: number;
|
|
@@ -4,7 +4,7 @@ import { LEAD_LANE_KEEP_ALIVE_BASE_BACKOFF_MS, LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_M
|
|
|
4
4
|
import { LanesError } from "./errors.js";
|
|
5
5
|
import { laneRunDir } from "./spawn.js";
|
|
6
6
|
import { compareEnvelopeNames } from "./envelope-sequence.js";
|
|
7
|
-
import { listQuestionIds, readAllLanes, tryReadQuestion } from "./store.js";
|
|
7
|
+
import { laneRecordPath, listQuestionIds, readAllLanes, tryReadQuestion } from "./store.js";
|
|
8
8
|
import { isIsoDateTime } from "./types.js";
|
|
9
9
|
function readTerminalClassifications(hqRoot, laneId) {
|
|
10
10
|
const root = fs.realpathSync(hqRoot);
|
|
@@ -62,6 +62,7 @@ const VERDICTS = [
|
|
|
62
62
|
"restarted_untimed",
|
|
63
63
|
"keep_alive_off",
|
|
64
64
|
"not_restarted",
|
|
65
|
+
"stranded",
|
|
65
66
|
"waiting",
|
|
66
67
|
];
|
|
67
68
|
function parseTime(value) {
|
|
@@ -70,6 +71,17 @@ function parseTime(value) {
|
|
|
70
71
|
const parsed = Date.parse(value);
|
|
71
72
|
return Number.isFinite(parsed) ? parsed : undefined;
|
|
72
73
|
}
|
|
74
|
+
function isStrandedKeepAliveLead(lane) {
|
|
75
|
+
return lane.depth === 0 && lane.keep_alive === true && (lane.state === "failed" ||
|
|
76
|
+
(lane.state === "interrupted" && lane.terminal_reason === "account_quota_exhausted"));
|
|
77
|
+
}
|
|
78
|
+
function strandedTerminalTime(lane) {
|
|
79
|
+
if (lane.state === "interrupted")
|
|
80
|
+
return parseTime(lane.interrupted_at);
|
|
81
|
+
if (lane.state === "failed")
|
|
82
|
+
return parseTime(lane.terminal_details?.failed_at);
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
73
85
|
function readLaneWorkerResumeEvents(hqRoot, laneId) {
|
|
74
86
|
const root = fs.realpathSync(hqRoot);
|
|
75
87
|
const eventsPath = path.join(laneRunDir(root, laneId), "watcher", "events.jsonl");
|
|
@@ -491,9 +503,45 @@ export function buildKeepAliveAudit(input) {
|
|
|
491
503
|
.filter((entry) => entry !== null)
|
|
492
504
|
.sort((a, b) => a.lane.localeCompare(b.lane));
|
|
493
505
|
const lanes = allLanes.filter((lane) => (input.laneId === undefined || lane.lane_id === input.laneId) &&
|
|
494
|
-
typeof lane.hire_grant === "number" && Number.isFinite(lane.hire_grant) && lane.hire_grant > 0);
|
|
506
|
+
(isStrandedKeepAliveLead(lane) || (typeof lane.hire_grant === "number" && Number.isFinite(lane.hire_grant) && lane.hire_grant > 0)));
|
|
495
507
|
const rows = [];
|
|
496
508
|
const errors = [];
|
|
509
|
+
for (const lane of lanes) {
|
|
510
|
+
if (!isStrandedKeepAliveLead(lane))
|
|
511
|
+
continue;
|
|
512
|
+
const terminalAt = strandedTerminalTime(lane);
|
|
513
|
+
if (terminalAt === undefined) {
|
|
514
|
+
errors.push({
|
|
515
|
+
lane: lane.lane_id,
|
|
516
|
+
error: "keep_alive_audit_terminal_time_missing",
|
|
517
|
+
path: laneRecordPath(input.hqRoot, lane.lane_id),
|
|
518
|
+
message: `Cannot determine terminal transition time for stranded keep-alive lead ${lane.lane_id}.`,
|
|
519
|
+
});
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
if (terminalAt < sinceMs || terminalAt > nowMs)
|
|
523
|
+
continue;
|
|
524
|
+
const dueCandidates = [
|
|
525
|
+
{ at: parseTime(lane.last_envelope?.next_check_at), reason: "next_check_at" },
|
|
526
|
+
{ at: parseTime(lane.keep_alive_next_eligible_at), reason: "backoff" },
|
|
527
|
+
{ at: parseTime(lane.terminal_details?.quota_reset_at) ?? parseTime(lane.quota_reset_at), reason: "quota_reset_at" },
|
|
528
|
+
].filter((candidate) => candidate.at !== undefined);
|
|
529
|
+
dueCandidates.sort((a, b) => b.at - a.at);
|
|
530
|
+
const due = dueCandidates[0] ?? { at: terminalAt, reason: "backoff" };
|
|
531
|
+
rows.push({
|
|
532
|
+
lane: lane.lane_id,
|
|
533
|
+
project: lane.project_id,
|
|
534
|
+
profile: lane.worker_profile ?? lane.worker ?? null,
|
|
535
|
+
stopped_at: iso(terminalAt),
|
|
536
|
+
next_check_at: lane.last_envelope?.next_check_at ?? null,
|
|
537
|
+
due_at: iso(due.at),
|
|
538
|
+
due_reason: due.reason,
|
|
539
|
+
restart: null,
|
|
540
|
+
verdict: "stranded",
|
|
541
|
+
...(lane.terminal_reason === undefined ? {} : { terminal_reason: lane.terminal_reason }),
|
|
542
|
+
remediation: `Ask the senior to run: hq lanes resume ${lane.lane_id} --from-failed --reason "recover stranded keep-alive lane"`,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
497
545
|
for (const lane of lanes) {
|
|
498
546
|
try {
|
|
499
547
|
const { allMtimes, stops } = readStopEnvelopes(input.hqRoot, lane.lane_id);
|
|
@@ -561,6 +609,7 @@ export function buildKeepAliveAudit(input) {
|
|
|
561
609
|
summary: {
|
|
562
610
|
by_verdict: byVerdict,
|
|
563
611
|
not_restarted: byVerdict.not_restarted,
|
|
612
|
+
stranded: byVerdict.stranded,
|
|
564
613
|
restarted_untimed: byVerdict.restarted_untimed,
|
|
565
614
|
late_restarts_after_window: lateRestartsAfterWindow,
|
|
566
615
|
late_over_10m: lateOverTenMinutes,
|
|
@@ -592,8 +641,9 @@ export function formatKeepAliveAuditText(result) {
|
|
|
592
641
|
...values.map(tableLine),
|
|
593
642
|
"",
|
|
594
643
|
`Since ${result.since}; ${counts}`,
|
|
595
|
-
`not_restarted=${result.summary.not_restarted}, late_restarts_after_window=${result.summary.late_restarts_after_window}, late_over_10m=${result.summary.late_over_10m}, early_wake_by_others=${result.summary.early_wake_by_others}, self_wake=${result.summary.self_wake}`,
|
|
644
|
+
`not_restarted=${result.summary.not_restarted}, stranded=${result.summary.stranded}, late_restarts_after_window=${result.summary.late_restarts_after_window}, late_over_10m=${result.summary.late_over_10m}, early_wake_by_others=${result.summary.early_wake_by_others}, self_wake=${result.summary.self_wake}`,
|
|
596
645
|
`Stopped and not restarted on their own: ${result.summary.stopped_and_not_restarted_on_their_own}`,
|
|
646
|
+
...result.rows.filter((row) => row.remediation).map((row) => `Stranded ${row.lane}: ${row.remediation}`),
|
|
597
647
|
...result.companyless_lead_lanes.map((lane) => `Company-less lead ${lane.lane} (${lane.project}): ${lane.reason}; repair with hq lanes repair-company ${lane.lane} --company <slug>`),
|
|
598
648
|
...result.errors.map((error) => `Error ${error.lane} (${error.error}) at ${error.path}: ${error.message}`),
|
|
599
649
|
].join("\n");
|
package/dist/lib/lanes/spawn.js
CHANGED
|
@@ -1511,7 +1511,7 @@ export function markSpawnFailure(hqRoot, laneId, reasonRaw, details, options = {
|
|
|
1511
1511
|
queued_at: undefined,
|
|
1512
1512
|
admission_reserved_at: undefined,
|
|
1513
1513
|
terminal_reason: reason,
|
|
1514
|
-
terminal_details: details,
|
|
1514
|
+
terminal_details: resumableQuotaHold ? details : { ...details, failed_at: new Date().toISOString() },
|
|
1515
1515
|
...(resumableQuotaHold
|
|
1516
1516
|
? {
|
|
1517
1517
|
...(options.pendingResume !== undefined
|
package/dist/sentry-init.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { NodeOptions } from "@sentry/node";
|
|
|
2
2
|
type SentryModule = typeof import("@sentry/node");
|
|
3
3
|
type SentryBreadcrumb = Parameters<SentryModule["addBreadcrumb"]>[0];
|
|
4
4
|
/** Shared Sentry SDK setup for the command graph and isolated fast handlers. */
|
|
5
|
+
export declare function createFlaggedBeforeSend(beforeSend: NodeOptions["beforeSend"], skipManualOnlyReportsEnabled?: () => Promise<boolean>): NonNullable<NodeOptions["beforeSend"]>;
|
|
5
6
|
export declare function initSentryRuntime(beforeSend: NodeOptions["beforeSend"], overrides?: Partial<Pick<NodeOptions, "transport">>, pendingBreadcrumbs?: readonly SentryBreadcrumb[]): SentryModule | undefined;
|
|
6
7
|
export declare function finishSentryRuntimeSession(): void;
|
|
7
8
|
export declare function addSentryRuntimeBreadcrumb(breadcrumb: SentryBreadcrumb): void;
|
package/dist/sentry-init.js
CHANGED
|
@@ -3,13 +3,116 @@ import { BUNDLED_DSN } from "./sentry-dsn.generated.js";
|
|
|
3
3
|
import { CLI_VERSION } from "./cli-version.js";
|
|
4
4
|
import { beforeBreadcrumb } from "./utils/breadcrumb-buffer.js";
|
|
5
5
|
import { getCachedSentryUser } from "./utils/sentry-identity.js";
|
|
6
|
-
const
|
|
6
|
+
const requireRuntime = createRequire(import.meta.url);
|
|
7
7
|
let sentryModule;
|
|
8
8
|
function loadSentryModule() {
|
|
9
|
-
sentryModule ??=
|
|
9
|
+
sentryModule ??= requireRuntime("@sentry/node");
|
|
10
10
|
return sentryModule;
|
|
11
11
|
}
|
|
12
12
|
/** Shared Sentry SDK setup for the command graph and isolated fast handlers. */
|
|
13
|
+
export function createFlaggedBeforeSend(beforeSend, skipManualOnlyReportsEnabled = resolveManualOnlyReportSkip) {
|
|
14
|
+
return async (event, hint) => {
|
|
15
|
+
if (event.tags?.sentry_event_type === "client_health" &&
|
|
16
|
+
event.tags.fix_outcome === "not_auto_fixable") {
|
|
17
|
+
const checkIds = clientHealthCheckIds(event);
|
|
18
|
+
// Sync-family reports detect an open sync P0. Keep filing them until
|
|
19
|
+
// the operator declares that P0 closed.
|
|
20
|
+
if (checkIds.length === 0 ||
|
|
21
|
+
checkIds.some((id) => MANUAL_ONLY_DROP_FAMILY_PREFIXES.some((prefix) => matchesCheckFamily(id, prefix)))) {
|
|
22
|
+
return beforeSend ? beforeSend(event, hint) : event;
|
|
23
|
+
}
|
|
24
|
+
let skipManualOnlyReport = false;
|
|
25
|
+
try {
|
|
26
|
+
skipManualOnlyReport = await skipManualOnlyReportsEnabled();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// The default-off gate fails open to the prior reporting behavior.
|
|
30
|
+
process.stderr.write("HQ client-health manual-only report flag lookup failed; keeping current reporting behavior.\n");
|
|
31
|
+
}
|
|
32
|
+
if (skipManualOnlyReport) {
|
|
33
|
+
recordSkippedManualOnlyReport(checkIds);
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return beforeSend ? beforeSend(event, hint) : event;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const MANUAL_ONLY_DROP_FAMILY_PREFIXES = [
|
|
41
|
+
"sync.manifest",
|
|
42
|
+
"sync.runtime",
|
|
43
|
+
"sync.journal",
|
|
44
|
+
];
|
|
45
|
+
function clientHealthCheckIds(event) {
|
|
46
|
+
try {
|
|
47
|
+
const tags = event.tags;
|
|
48
|
+
const extra = event.extra;
|
|
49
|
+
const rawValues = [
|
|
50
|
+
tags?.check_ids,
|
|
51
|
+
tags?.check_id,
|
|
52
|
+
tags?.checkIds,
|
|
53
|
+
tags?.checkId,
|
|
54
|
+
tags?.check_class,
|
|
55
|
+
extra?.check_ids,
|
|
56
|
+
extra?.check_id,
|
|
57
|
+
extra?.checkIds,
|
|
58
|
+
extra?.checkId,
|
|
59
|
+
];
|
|
60
|
+
const ids = new Set();
|
|
61
|
+
let hasUnparseableValue = false;
|
|
62
|
+
for (const raw of rawValues) {
|
|
63
|
+
if (raw === undefined || raw === null)
|
|
64
|
+
continue;
|
|
65
|
+
const candidates = Array.isArray(raw)
|
|
66
|
+
? raw
|
|
67
|
+
: typeof raw === "string"
|
|
68
|
+
? raw.split(/[\s,]+/).filter(Boolean)
|
|
69
|
+
: [];
|
|
70
|
+
if (candidates.length === 0) {
|
|
71
|
+
hasUnparseableValue = true;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
for (const candidate of candidates) {
|
|
75
|
+
if (typeof candidate === "string" &&
|
|
76
|
+
/^[A-Za-z0-9][A-Za-z0-9_-]*(?:\.[A-Za-z0-9][A-Za-z0-9_-]*)+$/.test(candidate)) {
|
|
77
|
+
ids.add(candidate);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
hasUnparseableValue = true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return hasUnparseableValue ? [] : [...ids];
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function matchesCheckFamily(checkId, prefix) {
|
|
91
|
+
return checkId === prefix || checkId.startsWith(`${prefix}.`);
|
|
92
|
+
}
|
|
93
|
+
function recordSkippedManualOnlyReport(checkIds) {
|
|
94
|
+
try {
|
|
95
|
+
const families = new Set();
|
|
96
|
+
for (const checkId of checkIds) {
|
|
97
|
+
for (const prefix of MANUAL_ONLY_DROP_FAMILY_PREFIXES) {
|
|
98
|
+
if (matchesCheckFamily(checkId, prefix)) {
|
|
99
|
+
families.add(prefix);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const familyList = [...families].sort().join(", ") || "unknown";
|
|
104
|
+
process.stderr.write(`HQ client-health manual-only report skipped; check families: ${familyList}.\n`);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// A local diagnostic must not change the decision to drop this event.
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function resolveManualOnlyReportSkip() {
|
|
111
|
+
// Keep this runtime lookup out of native fast-entry bundles. A static or
|
|
112
|
+
// dynamic import pulls the flag client and its fetch usage into the closure.
|
|
113
|
+
const flagModule = requireRuntime("./lib/doctor/client-health-reason-codes-flag.js");
|
|
114
|
+
return flagModule.clientHealthSkipManualOnlyReportsEnabled();
|
|
115
|
+
}
|
|
13
116
|
export function initSentryRuntime(beforeSend, overrides = {}, pendingBreadcrumbs = []) {
|
|
14
117
|
const dsn = BUNDLED_DSN || process.env.SENTRY_DSN;
|
|
15
118
|
if (!dsn)
|
|
@@ -23,7 +126,7 @@ export function initSentryRuntime(beforeSend, overrides = {}, pendingBreadcrumbs
|
|
|
23
126
|
release: `hq-cli@${CLI_VERSION}`,
|
|
24
127
|
environment: process.env.HQ_CLI_ENV ?? "production",
|
|
25
128
|
initialScope: { tags: { repo: "hq-cli" } },
|
|
26
|
-
beforeSend,
|
|
129
|
+
beforeSend: createFlaggedBeforeSend(beforeSend),
|
|
27
130
|
beforeBreadcrumb,
|
|
28
131
|
integrations: (defaults) => defaults.filter((integration) => integration.name !== "ProcessSession"),
|
|
29
132
|
...overrides,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { releaseCurrentPackageUseLease, releasePackageUseLeaseAfterResidentPreload, } from "./package-use-lease.js";
|
|
2
2
|
/** Runtime modules that resident commands may need after a wait starts. */
|
|
3
3
|
export const RESIDENT_PACKAGE_MODULES = {
|
|
4
|
-
"agent-kit": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
5
|
-
"agent-mcp": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "../commands/access.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "../utils/local-files-overview.js", "@indigoai-us/hq-cloud"],
|
|
4
|
+
"agent-kit": ["../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
5
|
+
"agent-mcp": ["../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "../commands/access.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "../utils/local-files-overview.js", "@indigoai-us/hq-cloud"],
|
|
6
6
|
login: [
|
|
7
7
|
"../lib/mesh/api.js",
|
|
8
8
|
"../lib/work-context/config.js",
|
|
@@ -10,7 +10,7 @@ export const RESIDENT_PACKAGE_MODULES = {
|
|
|
10
10
|
"../lib/work-context/paths.js",
|
|
11
11
|
"../lib/plan-limit-nag.js",
|
|
12
12
|
"../utils/plan-gate-error.js",
|
|
13
|
-
"../sentry.js",
|
|
13
|
+
"../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client",
|
|
14
14
|
"open",
|
|
15
15
|
"../utils/cognito-session.js",
|
|
16
16
|
"../lib/first-sync-next-step.js",
|
|
@@ -21,16 +21,16 @@ export const RESIDENT_PACKAGE_MODULES = {
|
|
|
21
21
|
"../lib/daemon/mcp-oauth.js",
|
|
22
22
|
"../lib/plan-limit-nag.js",
|
|
23
23
|
"../utils/plan-gate-error.js",
|
|
24
|
-
"../sentry.js",
|
|
24
|
+
"../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client",
|
|
25
25
|
"open",
|
|
26
26
|
"../utils/cognito-session.js",
|
|
27
27
|
"@indigoai-us/hq-cloud",
|
|
28
28
|
],
|
|
29
|
-
"agents-terminal": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
29
|
+
"agents-terminal": ["../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
30
30
|
bot: [
|
|
31
31
|
"../lib/flag-registry.js",
|
|
32
32
|
"../commands/access.js",
|
|
33
|
-
"../sentry.js",
|
|
33
|
+
"../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client",
|
|
34
34
|
"../lib/plan-limit-nag.js",
|
|
35
35
|
"../utils/plan-gate-error.js",
|
|
36
36
|
"open",
|
|
@@ -40,12 +40,12 @@ export const RESIDENT_PACKAGE_MODULES = {
|
|
|
40
40
|
"@indigoai-us/hq-cloud",
|
|
41
41
|
],
|
|
42
42
|
"bot-logs-follow": [],
|
|
43
|
-
mcp: ["../lib/mcp/tools.js", "../lib/mcp/prompts.js", "../utils/cognito-session.js", "@indigoai-us/hq-cloud", "../sentry.js"],
|
|
43
|
+
mcp: ["../lib/mcp/tools.js", "../lib/mcp/prompts.js", "../utils/cognito-session.js", "@indigoai-us/hq-cloud", "../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client"],
|
|
44
44
|
childWrapper: ["../main.js"],
|
|
45
45
|
coreJobRun: ["../lib/flag-registry.js"],
|
|
46
46
|
"daemon-call": ["../lib/daemon/protocol.js"],
|
|
47
47
|
daemon: [
|
|
48
|
-
"../sentry.js",
|
|
48
|
+
"../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client",
|
|
49
49
|
"../utils/cognito-session.js",
|
|
50
50
|
"../lib/plan-limit-nag.js",
|
|
51
51
|
"../utils/plan-gate-error.js",
|
|
@@ -58,17 +58,17 @@ export const RESIDENT_PACKAGE_MODULES = {
|
|
|
58
58
|
"@indigoai-us/hq-cloud",
|
|
59
59
|
],
|
|
60
60
|
"daemon-logs-follow": [],
|
|
61
|
-
"mesh-daemon": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
62
|
-
"lanes-watch": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
63
|
-
"monitor-wait": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
64
|
-
"monitor-run": ["../commands/lanes.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "../sentry.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
65
|
-
"agents-message-wait": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
66
|
-
"integrations-oauth-wait": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
67
|
-
"outpost-wait": ["../commands/lanes.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "../sentry.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
61
|
+
"mesh-daemon": ["../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
62
|
+
"lanes-watch": ["../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
63
|
+
"monitor-wait": ["../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
64
|
+
"monitor-run": ["../commands/lanes.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
65
|
+
"agents-message-wait": ["../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
66
|
+
"integrations-oauth-wait": ["../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
67
|
+
"outpost-wait": ["../commands/lanes.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client", "open", "../utils/cognito-session.js", "../lib/first-sync-next-step.js", "@indigoai-us/hq-cloud"],
|
|
68
68
|
"secrets-sandbox": [],
|
|
69
|
-
"pkg-update": ["../sentry.js", "../utils/cognito-session.js", "@indigoai-us/hq-cloud"],
|
|
69
|
+
"pkg-update": ["../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client", "../utils/cognito-session.js", "@indigoai-us/hq-cloud"],
|
|
70
70
|
"pack-install": [
|
|
71
|
-
"../sentry.js",
|
|
71
|
+
"../sentry.js", "../lib/doctor/client-health-reason-codes-flag.js", "@indigoai-us/hq-flags-client",
|
|
72
72
|
"../lib/plan-limit-nag.js",
|
|
73
73
|
"../utils/plan-gate-error.js",
|
|
74
74
|
"open",
|
|
@@ -87,6 +87,8 @@ const LOADERS = {
|
|
|
87
87
|
"agent-kit": [
|
|
88
88
|
async () => {
|
|
89
89
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
90
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
91
|
+
await import("@indigoai-us/hq-flags-client");
|
|
90
92
|
preloadSentrySdk();
|
|
91
93
|
},
|
|
92
94
|
() => import("../lib/plan-limit-nag.js"),
|
|
@@ -99,6 +101,8 @@ const LOADERS = {
|
|
|
99
101
|
"agent-mcp": [
|
|
100
102
|
async () => {
|
|
101
103
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
104
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
105
|
+
await import("@indigoai-us/hq-flags-client");
|
|
102
106
|
preloadSentrySdk();
|
|
103
107
|
},
|
|
104
108
|
() => import("../lib/plan-limit-nag.js"),
|
|
@@ -119,6 +123,8 @@ const LOADERS = {
|
|
|
119
123
|
() => import("../utils/plan-gate-error.js"),
|
|
120
124
|
async () => {
|
|
121
125
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
126
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
127
|
+
await import("@indigoai-us/hq-flags-client");
|
|
122
128
|
preloadSentrySdk();
|
|
123
129
|
},
|
|
124
130
|
() => import("open"),
|
|
@@ -133,6 +139,8 @@ const LOADERS = {
|
|
|
133
139
|
() => import("../utils/plan-gate-error.js"),
|
|
134
140
|
async () => {
|
|
135
141
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
142
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
143
|
+
await import("@indigoai-us/hq-flags-client");
|
|
136
144
|
preloadSentrySdk();
|
|
137
145
|
},
|
|
138
146
|
() => import("open"),
|
|
@@ -142,6 +150,8 @@ const LOADERS = {
|
|
|
142
150
|
"agents-terminal": [
|
|
143
151
|
async () => {
|
|
144
152
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
153
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
154
|
+
await import("@indigoai-us/hq-flags-client");
|
|
145
155
|
preloadSentrySdk();
|
|
146
156
|
},
|
|
147
157
|
() => import("../lib/plan-limit-nag.js"),
|
|
@@ -156,6 +166,8 @@ const LOADERS = {
|
|
|
156
166
|
() => import("../commands/access.js"),
|
|
157
167
|
async () => {
|
|
158
168
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
169
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
170
|
+
await import("@indigoai-us/hq-flags-client");
|
|
159
171
|
preloadSentrySdk();
|
|
160
172
|
},
|
|
161
173
|
() => import("../lib/plan-limit-nag.js"),
|
|
@@ -174,6 +186,8 @@ const LOADERS = {
|
|
|
174
186
|
() => import("@indigoai-us/hq-cloud"),
|
|
175
187
|
async () => {
|
|
176
188
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
189
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
190
|
+
await import("@indigoai-us/hq-flags-client");
|
|
177
191
|
preloadSentrySdk();
|
|
178
192
|
},
|
|
179
193
|
],
|
|
@@ -189,6 +203,8 @@ const LOADERS = {
|
|
|
189
203
|
daemon: [
|
|
190
204
|
async () => {
|
|
191
205
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
206
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
207
|
+
await import("@indigoai-us/hq-flags-client");
|
|
192
208
|
preloadSentrySdk();
|
|
193
209
|
},
|
|
194
210
|
() => import("../utils/cognito-session.js"),
|
|
@@ -206,6 +222,8 @@ const LOADERS = {
|
|
|
206
222
|
"mesh-daemon": [
|
|
207
223
|
async () => {
|
|
208
224
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
225
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
226
|
+
await import("@indigoai-us/hq-flags-client");
|
|
209
227
|
preloadSentrySdk();
|
|
210
228
|
},
|
|
211
229
|
() => import("../lib/plan-limit-nag.js"),
|
|
@@ -218,6 +236,8 @@ const LOADERS = {
|
|
|
218
236
|
"lanes-watch": [
|
|
219
237
|
async () => {
|
|
220
238
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
239
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
240
|
+
await import("@indigoai-us/hq-flags-client");
|
|
221
241
|
preloadSentrySdk();
|
|
222
242
|
},
|
|
223
243
|
() => import("../lib/plan-limit-nag.js"),
|
|
@@ -230,6 +250,8 @@ const LOADERS = {
|
|
|
230
250
|
"monitor-wait": [
|
|
231
251
|
async () => {
|
|
232
252
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
253
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
254
|
+
await import("@indigoai-us/hq-flags-client");
|
|
233
255
|
preloadSentrySdk();
|
|
234
256
|
},
|
|
235
257
|
() => import("../lib/plan-limit-nag.js"),
|
|
@@ -245,6 +267,8 @@ const LOADERS = {
|
|
|
245
267
|
() => import("../utils/plan-gate-error.js"),
|
|
246
268
|
async () => {
|
|
247
269
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
270
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
271
|
+
await import("@indigoai-us/hq-flags-client");
|
|
248
272
|
preloadSentrySdk();
|
|
249
273
|
},
|
|
250
274
|
() => import("open"),
|
|
@@ -255,6 +279,8 @@ const LOADERS = {
|
|
|
255
279
|
"agents-message-wait": [
|
|
256
280
|
async () => {
|
|
257
281
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
282
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
283
|
+
await import("@indigoai-us/hq-flags-client");
|
|
258
284
|
preloadSentrySdk();
|
|
259
285
|
},
|
|
260
286
|
() => import("../lib/plan-limit-nag.js"),
|
|
@@ -267,6 +293,8 @@ const LOADERS = {
|
|
|
267
293
|
"integrations-oauth-wait": [
|
|
268
294
|
async () => {
|
|
269
295
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
296
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
297
|
+
await import("@indigoai-us/hq-flags-client");
|
|
270
298
|
preloadSentrySdk();
|
|
271
299
|
},
|
|
272
300
|
() => import("../lib/plan-limit-nag.js"),
|
|
@@ -282,6 +310,8 @@ const LOADERS = {
|
|
|
282
310
|
() => import("../utils/plan-gate-error.js"),
|
|
283
311
|
async () => {
|
|
284
312
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
313
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
314
|
+
await import("@indigoai-us/hq-flags-client");
|
|
285
315
|
preloadSentrySdk();
|
|
286
316
|
},
|
|
287
317
|
() => import("open"),
|
|
@@ -293,6 +323,8 @@ const LOADERS = {
|
|
|
293
323
|
"pkg-update": [
|
|
294
324
|
async () => {
|
|
295
325
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
326
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
327
|
+
await import("@indigoai-us/hq-flags-client");
|
|
296
328
|
preloadSentrySdk();
|
|
297
329
|
},
|
|
298
330
|
() => import("../utils/cognito-session.js"),
|
|
@@ -301,6 +333,8 @@ const LOADERS = {
|
|
|
301
333
|
"pack-install": [
|
|
302
334
|
async () => {
|
|
303
335
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
336
|
+
await import("../lib/doctor/client-health-reason-codes-flag.js");
|
|
337
|
+
await import("@indigoai-us/hq-flags-client");
|
|
304
338
|
preloadSentrySdk();
|
|
305
339
|
},
|
|
306
340
|
() => import("../lib/plan-limit-nag.js"),
|