@indigoai-us/hq-cli 5.345.19 → 5.345.21
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 +9 -0
- package/assets/scaffold/core/scripts/hq-delegate-secrets.sh +1 -1
- package/assets/scaffold/core/scripts/lib/transcript-tail.sh +33 -23
- package/assets/scaffold/core/scripts/ontology-garden.mjs +86 -3
- package/assets/scaffold/core/scripts/ontology-source-worklist.sh +141 -4
- package/assets/scaffold/core/scripts/register-project.sh +82 -14
- package/assets/scaffold/core/scripts/resume-thread-lock.sh +5 -5
- package/assets/scaffold/core/scripts/share-suggestion-state.sh +10 -2
- package/assets/scaffold/core/scripts/work-mesh-live-bind-trusted.sh +36 -4
- package/assets/scaffold/core/scripts/worktree-gc.sh +1 -1
- package/dist/commands/core-ontology-native.js +110 -4
- package/dist/commands/core-register-project-native.js +36 -3
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.js +78 -4
- package/dist/commands/integrations-connect.js +7 -3
- package/dist/commands/lanes.js +40 -26
- package/dist/lib/bot/continuity-download.js +4 -1
- package/dist/lib/core-utils/sentry-report.js +69 -13
- package/dist/lib/doctor/client-health-reason-codes-flag.d.ts +6 -0
- package/dist/lib/doctor/client-health-reason-codes-flag.js +28 -7
- package/dist/lib/doctor/json-output.d.ts +4 -0
- package/dist/lib/doctor/json-output.js +5 -2
- package/dist/lib/lanes/autopilot.js +5 -1
- package/dist/lib/lanes/grants.d.ts +3 -0
- package/dist/lib/lanes/grants.js +10 -3
- package/dist/lib/lanes/index.d.ts +1 -1
- package/dist/lib/lanes/index.js +1 -1
- package/dist/utils/safe-caught-error.d.ts +3 -0
- package/dist/utils/safe-caught-error.js +16 -0
- package/package.json +1 -1
|
@@ -48,6 +48,7 @@ const SENTRY_REPORT_LEVELS = new Set([
|
|
|
48
48
|
const HOOK_TIMEOUT_WARNING_TYPE = "hook_timeout_warning";
|
|
49
49
|
const HOOK_TIMEOUT_EXCEEDED_TYPE = "hook_timeout_exceeded";
|
|
50
50
|
const HOOK_LATE_FINISH_TYPE = "hook_late_finish";
|
|
51
|
+
const HOOK_TIMEOUT_FINGERPRINT = "hook-timeout";
|
|
51
52
|
const HOOK_TIMEOUT_WARNING_METRIC = "hq.hook_timeout_warning";
|
|
52
53
|
const HOOK_TIMEOUT_EXCEEDED_METRIC = "hq.hook_timeout_exceeded";
|
|
53
54
|
const MAX_HOOK_SEQUENCE_ENTRIES = 20;
|
|
@@ -275,6 +276,17 @@ function safeSlowPhase(value) {
|
|
|
275
276
|
return "unknown";
|
|
276
277
|
return SAFE_SLOW_PHASES.has(value) ? value : "other";
|
|
277
278
|
}
|
|
279
|
+
function resolvedSlowPhase(metadata) {
|
|
280
|
+
const reported = safeSlowPhase(metadata.slow_phase);
|
|
281
|
+
if (reported !== "unknown" && reported !== "other")
|
|
282
|
+
return reported;
|
|
283
|
+
// Which measured master-hook phase consumed the most time when the caller's
|
|
284
|
+
// active-phase field was absent or unusable?
|
|
285
|
+
const debugContext = metadata.hook_timeout_debug_context;
|
|
286
|
+
const slowestPhase = debugContext?.phase_timings
|
|
287
|
+
.reduce((slowest, timing) => slowest === undefined || timing.elapsed_ms > slowest.elapsed_ms ? timing : slowest, undefined);
|
|
288
|
+
return slowestPhase ? safeSlowPhase(slowestPhase.phase) : reported;
|
|
289
|
+
}
|
|
278
290
|
function boundedDebugHookEvent(value) {
|
|
279
291
|
return typeof value === "string" && DEBUG_CONTEXT_HOOK_EVENTS.has(value) ? value : "other";
|
|
280
292
|
}
|
|
@@ -534,6 +546,28 @@ function timeoutElapsedMs(metadata) {
|
|
|
534
546
|
function formatNumber(value) {
|
|
535
547
|
return value.toFixed(2).replace(/\.00$|(?<=\.[0-9])0$/, "");
|
|
536
548
|
}
|
|
549
|
+
function formatSeconds(milliseconds) {
|
|
550
|
+
return `${formatNumber(milliseconds / 1_000)}s`;
|
|
551
|
+
}
|
|
552
|
+
// How much of the declared timeout has elapsed, and how much remains?
|
|
553
|
+
function hookTimeoutMessage(event) {
|
|
554
|
+
if (event.type !== HOOK_TIMEOUT_WARNING_TYPE)
|
|
555
|
+
return event.message;
|
|
556
|
+
const elapsedMs = timeoutElapsedMs(event.metadata);
|
|
557
|
+
const declaredTimeoutMs = event.metadata.declared_timeout_ms;
|
|
558
|
+
if (elapsedMs === undefined ||
|
|
559
|
+
typeof declaredTimeoutMs !== "number" ||
|
|
560
|
+
!Number.isFinite(declaredTimeoutMs) ||
|
|
561
|
+
declaredTimeoutMs <= 0) {
|
|
562
|
+
return event.message;
|
|
563
|
+
}
|
|
564
|
+
const elapsed = formatSeconds(elapsedMs);
|
|
565
|
+
const budget = formatSeconds(declaredTimeoutMs);
|
|
566
|
+
if (elapsedMs >= declaredTimeoutMs) {
|
|
567
|
+
return `HQ hook used ${elapsed} of its ${budget} configured timeout (${formatSeconds(elapsedMs - declaredTimeoutMs)} over budget).`;
|
|
568
|
+
}
|
|
569
|
+
return `HQ hook used ${elapsed} of its ${budget} configured timeout (${formatSeconds(declaredTimeoutMs - elapsedMs)} remaining).`;
|
|
570
|
+
}
|
|
537
571
|
function elapsedBucket(elapsedMs) {
|
|
538
572
|
if (elapsedMs === undefined)
|
|
539
573
|
return "unknown";
|
|
@@ -672,9 +706,33 @@ function safeSlowChild(metadata) {
|
|
|
672
706
|
(typeof value !== "string" || value.length === 0 || value === "other")) {
|
|
673
707
|
return "entry-bash-startup";
|
|
674
708
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
709
|
+
return typeof value === "string" && SAFE_HOOK_SCRIPT.test(value) ? value : "other";
|
|
710
|
+
}
|
|
711
|
+
function resolvedSlowChild(metadata) {
|
|
712
|
+
const directName = safeSlowChild(metadata);
|
|
713
|
+
const directDuration = metadata.slow_child_ms;
|
|
714
|
+
const hasDirectDuration = typeof directDuration === "number" && Number.isFinite(directDuration) && directDuration >= 0;
|
|
715
|
+
if (directName !== "other") {
|
|
716
|
+
return { name: directName, ...(hasDirectDuration ? { durationMs: directDuration } : {}) };
|
|
717
|
+
}
|
|
718
|
+
const currentScript = hookScript(metadata);
|
|
719
|
+
const currentEvent = stringMetadata(metadata, "hook_event", "");
|
|
720
|
+
const sequence = metadata.hook_sequence;
|
|
721
|
+
if (Array.isArray(sequence)) {
|
|
722
|
+
// Which named hook from this event's serialized sequence took longest
|
|
723
|
+
// when dispatch could not directly name its slow child?
|
|
724
|
+
const slowestNamedHook = sequence
|
|
725
|
+
.filter((entry) => entry.event === currentEvent &&
|
|
726
|
+
entry.script !== currentScript &&
|
|
727
|
+
SAFE_HOOK_SCRIPT.test(entry.script) &&
|
|
728
|
+
Number.isFinite(entry.ms) &&
|
|
729
|
+
entry.ms >= 0)
|
|
730
|
+
.reduce((slowest, entry) => (slowest === undefined || entry.ms > slowest.ms ? entry : slowest), undefined);
|
|
731
|
+
if (slowestNamedHook) {
|
|
732
|
+
return { name: slowestNamedHook.script, durationMs: slowestNamedHook.ms };
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return { name: directName, ...(hasDirectDuration ? { durationMs: directDuration } : {}) };
|
|
678
736
|
}
|
|
679
737
|
function isMachineWideHookSlowness(metadata) {
|
|
680
738
|
const spawnMs = metadata.spawn_ms;
|
|
@@ -717,7 +775,7 @@ function hookTimeoutTags(event) {
|
|
|
717
775
|
const shell = stringMetadata(metadata, "shell");
|
|
718
776
|
const nproc = metadata.nproc;
|
|
719
777
|
const loadAverage = parsedLoadAverage(metadata);
|
|
720
|
-
const
|
|
778
|
+
const slowChild = resolvedSlowChild(metadata);
|
|
721
779
|
return {
|
|
722
780
|
hook_timeout_kind: hookTimeoutKind(event),
|
|
723
781
|
hook_event: hookEvent(metadata),
|
|
@@ -738,11 +796,9 @@ function hookTimeoutTags(event) {
|
|
|
738
796
|
load_bucket: typeof loadAverage === "string" ? loadAverage : loadBucket(loadAverage),
|
|
739
797
|
root_storage_class: rootStorageClassTag(metadata),
|
|
740
798
|
spawn_ms_bucket: spawnMsBucket(metadata),
|
|
741
|
-
slow_child:
|
|
742
|
-
slow_phase:
|
|
743
|
-
slow_child_ms_bucket:
|
|
744
|
-
? elapsedBucket(slowChildMs)
|
|
745
|
-
: "unknown",
|
|
799
|
+
slow_child: slowChild.name,
|
|
800
|
+
slow_phase: resolvedSlowPhase(metadata),
|
|
801
|
+
slow_child_ms_bucket: elapsedBucket(slowChild.durationMs),
|
|
746
802
|
machine_wide: isMachineWideHookSlowness(metadata) ? "true" : "false",
|
|
747
803
|
nproc: typeof nproc === "number" || typeof nproc === "string"
|
|
748
804
|
? safeTagValue(String(nproc), SAFE_NPROC)
|
|
@@ -777,7 +833,7 @@ function sentryEnvelope(event, overrides, additionalTags = {}) {
|
|
|
777
833
|
/** Build the same scrubbed event projection that the live SDK path uses. */
|
|
778
834
|
function scrubbedSentryEnvelope(event, overrides, additionalTags = {}) {
|
|
779
835
|
return beforeSend({
|
|
780
|
-
message: event
|
|
836
|
+
message: hookTimeoutMessage(event),
|
|
781
837
|
...sentryEnvelope(event, overrides, additionalTags),
|
|
782
838
|
}, {});
|
|
783
839
|
}
|
|
@@ -787,7 +843,7 @@ function hookTimeoutExceededOverrides(event) {
|
|
|
787
843
|
return undefined;
|
|
788
844
|
return {
|
|
789
845
|
level: "warning",
|
|
790
|
-
fingerprint: [
|
|
846
|
+
fingerprint: [HOOK_TIMEOUT_FINGERPRINT, hookTimeoutKind(event)],
|
|
791
847
|
};
|
|
792
848
|
}
|
|
793
849
|
/**
|
|
@@ -1090,7 +1146,7 @@ export async function reportSentryEvent(stdin, options = {}, dependencies = defa
|
|
|
1090
1146
|
: {};
|
|
1091
1147
|
const envelope = sentryEnvelope(event, overrides, { ...debugContextTags, ...tags });
|
|
1092
1148
|
try {
|
|
1093
|
-
dependencies.sentry.captureMessage(event
|
|
1149
|
+
dependencies.sentry.captureMessage(hookTimeoutMessage(event), envelope);
|
|
1094
1150
|
rateLimit.commitCapture();
|
|
1095
1151
|
if (await flushWithin(dependencies.sentry, timeoutMs))
|
|
1096
1152
|
return result;
|
|
@@ -1104,7 +1160,7 @@ export async function reportSentryEvent(stdin, options = {}, dependencies = defa
|
|
|
1104
1160
|
}
|
|
1105
1161
|
const envelope = sentryEnvelope(event, overrides, debugContextTags);
|
|
1106
1162
|
try {
|
|
1107
|
-
dependencies.sentry.captureMessage(event
|
|
1163
|
+
dependencies.sentry.captureMessage(hookTimeoutMessage(event), envelope);
|
|
1108
1164
|
if (await flushWithin(dependencies.sentry, timeoutMs))
|
|
1109
1165
|
return result;
|
|
1110
1166
|
}
|
|
@@ -2,6 +2,8 @@ import { type FlagClient, type FlagClientOptions } from "@indigoai-us/hq-flags-c
|
|
|
2
2
|
export declare const CLIENT_HEALTH_REASON_CODES_FLAG = "client-health-sync-reason-codes";
|
|
3
3
|
export declare const CLIENT_HEALTH_REASON_CODES_FLAG_FALLBACK = false;
|
|
4
4
|
export declare const CLIENT_HEALTH_REASON_CODES_FLAG_TIMEOUT_MS = 150;
|
|
5
|
+
export declare const CLIENT_HEALTH_CHECK_STATUS_FLAG = "client-health-sync-check-status";
|
|
6
|
+
export declare const CLIENT_HEALTH_CHECK_STATUS_FLAG_FALLBACK = false;
|
|
5
7
|
type ClientHealthFlagClient = Pick<FlagClient, "ready" | "isEnabled" | "close">;
|
|
6
8
|
export interface ClientHealthReasonCodesFlagDependencies {
|
|
7
9
|
env?: NodeJS.ProcessEnv;
|
|
@@ -21,7 +23,11 @@ export interface ClientHealthReasonCodesCompany {
|
|
|
21
23
|
* off so a flag outage cannot delay the doctor's health result.
|
|
22
24
|
*/
|
|
23
25
|
export declare function clientHealthReasonCodesEnabled(dependencies?: ClientHealthReasonCodesFlagDependencies, callerSignal?: AbortSignal): Promise<boolean>;
|
|
26
|
+
/** Resolve the separate status-report gate for the active company. */
|
|
27
|
+
export declare function clientHealthCheckStatusEnabled(dependencies?: ClientHealthReasonCodesFlagDependencies, callerSignal?: AbortSignal): Promise<boolean>;
|
|
24
28
|
/** Resolve the same default-off gate for one explicitly identified company. */
|
|
25
29
|
export declare function clientHealthReasonCodesEnabledForCompany(company: ClientHealthReasonCodesCompany, dependencies?: ClientHealthReasonCodesFlagDependencies, callerSignal?: AbortSignal): Promise<boolean>;
|
|
30
|
+
/** Resolve the separate default-off check-status gate for one company. */
|
|
31
|
+
export declare function clientHealthCheckStatusEnabledForCompany(company: ClientHealthReasonCodesCompany, dependencies?: ClientHealthReasonCodesFlagDependencies, callerSignal?: AbortSignal): Promise<boolean>;
|
|
26
32
|
export {};
|
|
27
33
|
//# sourceMappingURL=client-health-reason-codes-flag.d.ts.map
|
|
@@ -4,12 +4,14 @@ import { getClientHealthFlagToken } from "./client-health-flag-token.js";
|
|
|
4
4
|
export const CLIENT_HEALTH_REASON_CODES_FLAG = "client-health-sync-reason-codes";
|
|
5
5
|
export const CLIENT_HEALTH_REASON_CODES_FLAG_FALLBACK = false;
|
|
6
6
|
export const CLIENT_HEALTH_REASON_CODES_FLAG_TIMEOUT_MS = 150;
|
|
7
|
+
export const CLIENT_HEALTH_CHECK_STATUS_FLAG = "client-health-sync-check-status";
|
|
8
|
+
export const CLIENT_HEALTH_CHECK_STATUS_FLAG_FALLBACK = false;
|
|
7
9
|
function safeErrorClass(error) {
|
|
8
10
|
const name = error instanceof Error ? error.name : "UnknownError";
|
|
9
11
|
return /^[A-Za-z][A-Za-z0-9]{0,63}$/.test(name) ? name : "UnknownError";
|
|
10
12
|
}
|
|
11
13
|
function reportFlagFailure(error) {
|
|
12
|
-
process.stderr.write(`HQ client-health
|
|
14
|
+
process.stderr.write(`HQ client-health flag lookup failed (${safeErrorClass(error)}); using default off.\n`);
|
|
13
15
|
}
|
|
14
16
|
function combineSignals(first, second) {
|
|
15
17
|
return first ? AbortSignal.any([first, second]) : second;
|
|
@@ -30,13 +32,32 @@ export async function clientHealthReasonCodesEnabled(dependencies = {}, callerSi
|
|
|
30
32
|
companySlug: env.HQ_COMPANY_SLUG?.trim() || undefined,
|
|
31
33
|
}, dependencies, callerSignal);
|
|
32
34
|
}
|
|
35
|
+
/** Resolve the separate status-report gate for the active company. */
|
|
36
|
+
export async function clientHealthCheckStatusEnabled(dependencies = {}, callerSignal) {
|
|
37
|
+
const env = dependencies.env ?? process.env;
|
|
38
|
+
const companyUid = env.HQ_COMPANY_UID?.trim() || "";
|
|
39
|
+
if (!/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid)) {
|
|
40
|
+
return CLIENT_HEALTH_CHECK_STATUS_FLAG_FALLBACK;
|
|
41
|
+
}
|
|
42
|
+
return clientHealthCheckStatusEnabledForCompany({
|
|
43
|
+
companyUid,
|
|
44
|
+
companySlug: env.HQ_COMPANY_SLUG?.trim() || undefined,
|
|
45
|
+
}, dependencies, callerSignal);
|
|
46
|
+
}
|
|
33
47
|
/** Resolve the same default-off gate for one explicitly identified company. */
|
|
34
48
|
export async function clientHealthReasonCodesEnabledForCompany(company, dependencies = {}, callerSignal) {
|
|
49
|
+
return clientHealthFlagEnabledForCompany(CLIENT_HEALTH_REASON_CODES_FLAG, CLIENT_HEALTH_REASON_CODES_FLAG_FALLBACK, company, dependencies, callerSignal);
|
|
50
|
+
}
|
|
51
|
+
/** Resolve the separate default-off check-status gate for one company. */
|
|
52
|
+
export async function clientHealthCheckStatusEnabledForCompany(company, dependencies = {}, callerSignal) {
|
|
53
|
+
return clientHealthFlagEnabledForCompany(CLIENT_HEALTH_CHECK_STATUS_FLAG, CLIENT_HEALTH_CHECK_STATUS_FLAG_FALLBACK, company, dependencies, callerSignal);
|
|
54
|
+
}
|
|
55
|
+
async function clientHealthFlagEnabledForCompany(flagKey, fallback, company, dependencies, callerSignal) {
|
|
35
56
|
const env = dependencies.env ?? process.env;
|
|
36
57
|
const endpoint = resolveFlagRegistryEndpoint(undefined, env);
|
|
37
58
|
const companyUid = company.companyUid.trim();
|
|
38
|
-
if (!/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid)) {
|
|
39
|
-
return
|
|
59
|
+
if (!endpoint || !/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid)) {
|
|
60
|
+
return fallback;
|
|
40
61
|
}
|
|
41
62
|
const companySlug = company.companySlug?.trim() || "";
|
|
42
63
|
if (callerSignal?.aborted)
|
|
@@ -44,7 +65,7 @@ export async function clientHealthReasonCodesEnabledForCompany(company, dependen
|
|
|
44
65
|
let client;
|
|
45
66
|
let failed = false;
|
|
46
67
|
let failureReported = false;
|
|
47
|
-
let enabled =
|
|
68
|
+
let enabled = fallback;
|
|
48
69
|
const reportFailureOnce = (error) => {
|
|
49
70
|
failed = true;
|
|
50
71
|
if (failureReported)
|
|
@@ -83,8 +104,8 @@ export async function clientHealthReasonCodesEnabledForCompany(company, dependen
|
|
|
83
104
|
});
|
|
84
105
|
await client.ready();
|
|
85
106
|
if (!failed && !signal.aborted) {
|
|
86
|
-
enabled = client.isEnabled(
|
|
87
|
-
fallback
|
|
107
|
+
enabled = client.isEnabled(flagKey, {
|
|
108
|
+
fallback,
|
|
88
109
|
});
|
|
89
110
|
}
|
|
90
111
|
}
|
|
@@ -100,7 +121,7 @@ export async function clientHealthReasonCodesEnabledForCompany(company, dependen
|
|
|
100
121
|
}
|
|
101
122
|
}
|
|
102
123
|
return failed || signal.aborted
|
|
103
|
-
?
|
|
124
|
+
? fallback
|
|
104
125
|
: enabled;
|
|
105
126
|
}
|
|
106
127
|
//# sourceMappingURL=client-health-reason-codes-flag.js.map
|
|
@@ -54,6 +54,8 @@ export interface DoctorJsonResult {
|
|
|
54
54
|
reasonCode?: SyncReasonCode;
|
|
55
55
|
/** Present only when this result's company opted in through hq-flags. */
|
|
56
56
|
clientHealthReasonCodesEnabled?: true;
|
|
57
|
+
/** Present only when this result's company opted in to status reporting through hq-flags. */
|
|
58
|
+
clientHealthCheckStatusEnabled?: true;
|
|
57
59
|
/** What was inspected. Present only when the result carries one. */
|
|
58
60
|
target?: string;
|
|
59
61
|
/** Human-readable explanation. */
|
|
@@ -100,6 +102,8 @@ export interface BuildDoctorJsonInput {
|
|
|
100
102
|
scope?: readonly string[];
|
|
101
103
|
/** Check ids whose own company enabled diagnostic reason codes in hq-flags. */
|
|
102
104
|
clientHealthReasonCodesEnabledCheckIds?: ReadonlySet<string>;
|
|
105
|
+
/** Check ids whose own company enabled per-check status reporting in hq-flags. */
|
|
106
|
+
clientHealthCheckStatusEnabledCheckIds?: ReadonlySet<string>;
|
|
103
107
|
}
|
|
104
108
|
/** Assemble the machine-readable document from a completed run. */
|
|
105
109
|
export declare function buildDoctorJson(input: BuildDoctorJsonInput): DoctorJsonDocument;
|
|
@@ -34,7 +34,7 @@ export function buildDoctorJson(input) {
|
|
|
34
34
|
const results = [];
|
|
35
35
|
for (const run of input.families) {
|
|
36
36
|
for (const result of run.results) {
|
|
37
|
-
results.push(toJsonResult(run.family.id, result, input.clientHealthReasonCodesEnabledCheckIds));
|
|
37
|
+
results.push(toJsonResult(run.family.id, result, input.clientHealthReasonCodesEnabledCheckIds, input.clientHealthCheckStatusEnabledCheckIds));
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
return {
|
|
@@ -48,7 +48,7 @@ export function buildDoctorJson(input) {
|
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
/** Convert one {@link CheckResult} into its JSON form, omitting empty optionals. */
|
|
51
|
-
function toJsonResult(familyId, result, clientHealthReasonCodesEnabledCheckIds) {
|
|
51
|
+
function toJsonResult(familyId, result, clientHealthReasonCodesEnabledCheckIds, clientHealthCheckStatusEnabledCheckIds) {
|
|
52
52
|
const remediation = deriveRemediation(result);
|
|
53
53
|
return {
|
|
54
54
|
status: result.status,
|
|
@@ -58,6 +58,9 @@ function toJsonResult(familyId, result, clientHealthReasonCodesEnabledCheckIds)
|
|
|
58
58
|
...(clientHealthReasonCodesEnabledCheckIds?.has(result.checkId)
|
|
59
59
|
? { clientHealthReasonCodesEnabled: true }
|
|
60
60
|
: {}),
|
|
61
|
+
...(clientHealthCheckStatusEnabledCheckIds?.has(result.checkId)
|
|
62
|
+
? { clientHealthCheckStatusEnabled: true }
|
|
63
|
+
: {}),
|
|
61
64
|
...(result.target !== undefined ? { target: result.target } : {}),
|
|
62
65
|
message: result.message,
|
|
63
66
|
...(result.details !== undefined ? { details: result.details } : {}),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import { safeCaughtError } from "../../utils/safe-caught-error.js";
|
|
4
5
|
import { laneRunsOnHost } from "./admission.js";
|
|
5
6
|
import { isControllerAliasOutpostId } from "./admission-host.js";
|
|
6
7
|
import { LEAD_REGISTRY_REL, validateOutpostLeadRegistry } from "./autopilot-registry.js";
|
|
@@ -783,7 +784,10 @@ async function runLeadAutopilotPassUnlocked(input) {
|
|
|
783
784
|
input.outpostId === registered.host &&
|
|
784
785
|
lead.host.kind === "local" &&
|
|
785
786
|
lead.host.id === input.hostname &&
|
|
786
|
-
await (input.readFlag ?? readLanesRegistryFlag)(LEAD_AUTOPILOT_OUTPOST_NATIVE_LEADS_FLAG, { company }).catch(() =>
|
|
787
|
+
await (input.readFlag ?? readLanesRegistryFlag)(LEAD_AUTOPILOT_OUTPOST_NATIVE_LEADS_FLAG, { company }).catch((error) => {
|
|
788
|
+
console.warn("[hq-cli:lanes/autopilot] outpost-native lead flag read failed", safeCaughtError(error));
|
|
789
|
+
return false;
|
|
790
|
+
});
|
|
787
791
|
const hostMatchesRegistry = registeredLocalHost
|
|
788
792
|
? lead.host.kind === "local"
|
|
789
793
|
: outpostNativeLead ||
|
|
@@ -2,7 +2,10 @@ import { type LaneRecord } from "./types.js";
|
|
|
2
2
|
import { LanesError } from "./errors.js";
|
|
3
3
|
export type LaneHireGrantRow = Pick<LaneRecord, "lane_id" | "state" | "senior" | "hire_grant">;
|
|
4
4
|
export declare function isFinishedResumableHireChild(state: LaneRecord["state"]): boolean;
|
|
5
|
+
export declare function laneHoldsHireGrantCarve(lane: Pick<LaneHireGrantRow, "state">, finishedChildrenDoNotCount?: boolean): boolean;
|
|
5
6
|
export declare function aliveDirectChildren<T extends LaneHireGrantRow>(lanes: readonly T[], parentLaneId: string, finishedChildrenDoNotCount?: boolean): T[];
|
|
7
|
+
/** Children whose existing hire grants occupy their senior's carve. */
|
|
8
|
+
export declare function childrenHoldingHireGrantCarve<T extends LaneHireGrantRow>(lanes: readonly T[], parentLaneId: string, finishedChildrenDoNotCount?: boolean): T[];
|
|
6
9
|
export declare function assignedChildHireGrants(children: readonly LaneHireGrantRow[], exceptLaneId?: string): number;
|
|
7
10
|
export interface HireGrantAdmissionSnapshot {
|
|
8
11
|
parent: LaneHireGrantRow;
|
package/dist/lib/lanes/grants.js
CHANGED
|
@@ -4,11 +4,18 @@ import { LanesError } from "./errors.js";
|
|
|
4
4
|
export function isFinishedResumableHireChild(state) {
|
|
5
5
|
return state === "awaiting_input" || state === "blocked" || state === "done";
|
|
6
6
|
}
|
|
7
|
+
export function laneHoldsHireGrantCarve(lane, finishedChildrenDoNotCount = false) {
|
|
8
|
+
return !isTerminalLaneState(lane.state) &&
|
|
9
|
+
!(finishedChildrenDoNotCount && isFinishedResumableHireChild(lane.state));
|
|
10
|
+
}
|
|
7
11
|
export function aliveDirectChildren(lanes, parentLaneId, finishedChildrenDoNotCount = false) {
|
|
12
|
+
return childrenHoldingHireGrantCarve(lanes, parentLaneId, finishedChildrenDoNotCount);
|
|
13
|
+
}
|
|
14
|
+
/** Children whose existing hire grants occupy their senior's carve. */
|
|
15
|
+
export function childrenHoldingHireGrantCarve(lanes, parentLaneId, finishedChildrenDoNotCount = false) {
|
|
8
16
|
return lanes.filter((lane) => lane.senior.kind === "lane" &&
|
|
9
17
|
lane.senior.id === parentLaneId &&
|
|
10
|
-
|
|
11
|
-
!(finishedChildrenDoNotCount && isFinishedResumableHireChild(lane.state)));
|
|
18
|
+
laneHoldsHireGrantCarve(lane, finishedChildrenDoNotCount));
|
|
12
19
|
}
|
|
13
20
|
export function assignedChildHireGrants(children, exceptLaneId) {
|
|
14
21
|
return children.reduce((sum, child) => child.lane_id === exceptLaneId ? sum : sum + (child.hire_grant ?? 0), 0);
|
|
@@ -101,7 +108,7 @@ export function withHireGrantAdmission(hqRoot, parentLaneId, fn, suppliedHierarc
|
|
|
101
108
|
}
|
|
102
109
|
parent = suppliedParent;
|
|
103
110
|
}
|
|
104
|
-
const children =
|
|
111
|
+
const children = childrenHoldingHireGrantCarve(allLanes, parentLaneId, finishedChildrenDoNotCount);
|
|
105
112
|
const parentGrant = parent.hire_grant ?? 0;
|
|
106
113
|
const availableChildGrant = Math.max(0, parentGrant - assignedChildHireGrants(children));
|
|
107
114
|
return fn({ parent, children, parentGrant, availableChildGrant });
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { AccountResolutionError, LaneNotFoundError, LaneSchemaError, LaneVersionConflict, LanesConfigError, LanesError, WorkerResolutionError, isLaneVersionConflict, isLanesError, } from "./errors.js";
|
|
5
5
|
export { MAX_LANE_DEPTH } from "./constants.js";
|
|
6
|
-
export { assignedChildHireGrants, aliveDirectChildren, isFinishedResumableHireChild, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, } from "./grants.js";
|
|
6
|
+
export { assignedChildHireGrants, aliveDirectChildren, childrenHoldingHireGrantCarve, isFinishedResumableHireChild, laneHoldsHireGrantCarve, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, } from "./grants.js";
|
|
7
7
|
export type { HireGrantBackfillRow } from "./grants.js";
|
|
8
8
|
export { compareLaneQueueOrder, hasActiveAdmissionReservation, hasActivePendingResume, hasEarlierEligibleQueuedLane, laneOccupiesRunningSlot, laneAdmissionCapReason, laneRunsOnHost, withAllLaneAdmissionLocks, withLaneAdmissionLock, } from "./admission.js";
|
|
9
9
|
export type { AdmissionCapReason, AdmissionHost } from "./admission.js";
|
package/dist/lib/lanes/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { AccountResolutionError, LaneNotFoundError, LaneSchemaError, LaneVersionConflict, LanesConfigError, LanesError, WorkerResolutionError, isLaneVersionConflict, isLanesError, } from "./errors.js";
|
|
5
5
|
export { MAX_LANE_DEPTH } from "./constants.js";
|
|
6
|
-
export { assignedChildHireGrants, aliveDirectChildren, isFinishedResumableHireChild, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, } from "./grants.js";
|
|
6
|
+
export { assignedChildHireGrants, aliveDirectChildren, childrenHoldingHireGrantCarve, isFinishedResumableHireChild, laneHoldsHireGrantCarve, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, } from "./grants.js";
|
|
7
7
|
export { compareLaneQueueOrder, hasActiveAdmissionReservation, hasActivePendingResume, hasEarlierEligibleQueuedLane, laneOccupiesRunningSlot, laneAdmissionCapReason, laneRunsOnHost, withAllLaneAdmissionLocks, withLaneAdmissionLock, } from "./admission.js";
|
|
8
8
|
export { buildHostCapacitySnapshot, chooseHostForLane, parseHostCapacitySnapshot, } from "./host-capacity.js";
|
|
9
9
|
export { CONFIG_SCHEMA_VERSION, ENVELOPE_DECISIONS, ENVELOPE_SCHEMA_VERSION, HISTORY_CAP, HOST_KINDS, LANE_PROVIDERS, LANE_SCHEMA_VERSION, LANE_STORY_STATUSES, LANE_STATES, LIVENESS_STATES, QUESTION_BODY_STORES, QUESTION_SCHEMA_VERSION, QUESTION_STATUSES, QUEUED_REASONS, RAISED_VIA, ROUTING_REASONS, SENIOR_KINDS, STORY_STATUSES, TERMINAL_LANE_STATES, isEnvelopeDecision, isHostKind, isLaneProvider, isLaneState, isLivenessState, isQuestionStatus, isSeniorKind, isTerminalLaneState, parseLaneEnvelope, parseLaneProvider, parseLaneState, parseLivenessState, parseMajorSchema, parseQuestionStatus, parseSeniorKind, } from "./types.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { redactErrorText } from "./redact-error-text.js";
|
|
2
|
+
/** Render an unknown rejection safely for a concise diagnostic log. */
|
|
3
|
+
export function safeCaughtError(error) {
|
|
4
|
+
const rawName = error instanceof Error ? error.name : "UnknownError";
|
|
5
|
+
// A writable `name` can carry arbitrary text, so only class-shaped names are logged.
|
|
6
|
+
const name = /^[A-Za-z]{1,64}(?:Error|Exception)$/.test(rawName) ? rawName : "Error";
|
|
7
|
+
const rawMessage = error instanceof Error ? error.message : "non-Error rejection";
|
|
8
|
+
const message = redactErrorText(rawMessage)
|
|
9
|
+
.replace(/\b[a-z][a-z0-9+.-]*:\/\/[^\s"<>]+/gi, "[URL]")
|
|
10
|
+
.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, "[EMAIL]")
|
|
11
|
+
.replace(/\b(?:agt|cmp|prs|prj|session|sess|usr|user|acct|fin|st|e)_[A-Za-z0-9_-]+\b/gi, "[ID]")
|
|
12
|
+
.replace(/\b[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/gi, "[ID]")
|
|
13
|
+
.slice(0, 160);
|
|
14
|
+
return `${name}: ${message || "[empty]"}`;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=safe-caught-error.js.map
|