@indigoai-us/hq-cli 5.275.0 → 5.277.0
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 +112 -0
- package/README.md +12 -0
- package/assets/bot-workers/setup/context/USER-GUIDE.md +4 -4
- package/assets/scaffold/core/scripts/hq-delegate-pickup.sh +99 -0
- package/assets/scaffold/core/scripts/hq-delegate-repo.sh +158 -0
- package/assets/scaffold/core/scripts/hq-delegate-resolve.sh +145 -0
- package/assets/scaffold/core/scripts/hq-delegate-secrets.sh +129 -0
- package/assets/scaffold/core/scripts/hq-delegate-transfer.sh +175 -0
- package/assets/scaffold/core/scripts/lib/secret-patterns.sh +58 -0
- package/dist/command-catalog.generated.d.ts +239 -1
- package/dist/command-catalog.generated.js +302 -1
- package/dist/commands/core-checkpoint.d.ts +4 -2
- package/dist/commands/core-checkpoint.js +34 -3
- package/dist/commands/core-command-catalog.d.ts +4 -2
- package/dist/commands/core-command-catalog.js +5 -2
- package/dist/commands/core-delegate.d.ts +6 -0
- package/dist/commands/core-delegate.js +987 -0
- package/dist/commands/core-ontology.js +3 -3
- package/dist/commands/core.js +11 -1
- package/dist/commands/daemon.d.ts +4 -0
- package/dist/commands/daemon.js +175 -2
- package/dist/commands/dm.d.ts +2 -0
- package/dist/commands/dm.js +11 -4
- package/dist/commands/feedback.d.ts +6 -0
- package/dist/commands/feedback.js +86 -37
- package/dist/commands/lanes-accounts.js +44 -2
- package/dist/commands/lanes-message.js +69 -12
- package/dist/commands/lanes-resume.js +2 -0
- package/dist/commands/lanes-shared.d.ts +3 -0
- package/dist/commands/lanes-shared.js +12 -2
- package/dist/commands/lanes.d.ts +5 -1
- package/dist/commands/lanes.js +402 -49
- package/dist/commands/run-feature-telemetry.d.ts +3 -0
- package/dist/commands/run-feature-telemetry.js +6 -0
- package/dist/commands/run.js +3 -2
- package/dist/commands/secrets.d.ts +16 -1
- package/dist/commands/secrets.js +80 -45
- package/dist/commands/sync-mode.d.ts +11 -0
- package/dist/commands/sync-mode.js +30 -0
- package/dist/commands/whoami.d.ts +4 -6
- package/dist/commands/whoami.js +39 -8
- package/dist/fast-core.d.ts +6 -7
- package/dist/fast-core.js +22 -8
- package/dist/fast-scaffold.d.ts +33 -7
- package/dist/fast-scaffold.js +104 -8
- package/dist/index.js +32 -1
- package/dist/lanes-drain-hook.js +154 -7
- package/dist/lib/agent-kit/run/inbox.js +3 -2
- package/dist/lib/billing/plan-lock.d.ts +3 -1
- package/dist/lib/billing/plan-lock.js +7 -1
- package/dist/lib/checkpoint-ontology/apply.d.ts +6 -0
- package/dist/lib/checkpoint-ontology/apply.js +60 -3
- package/dist/lib/checkpoint-ontology/company.d.ts +8 -4
- package/dist/lib/checkpoint-ontology/company.js +15 -7
- package/dist/lib/checkpoint-ontology/plan.d.ts +36 -0
- package/dist/lib/checkpoint-ontology/plan.js +66 -4
- package/dist/lib/checkpoint-ontology/queued-touches.d.ts +32 -0
- package/dist/lib/checkpoint-ontology/queued-touches.js +193 -0
- package/dist/lib/checkpoint-ontology/tree.d.ts +2 -0
- package/dist/lib/checkpoint-ontology/tree.js +34 -0
- package/dist/lib/core-utils/jq-json-serializer.d.ts +5 -0
- package/dist/lib/core-utils/jq-json-serializer.js +10 -0
- package/dist/lib/core-utils/secret-patterns.d.ts +41 -0
- package/dist/lib/core-utils/secret-patterns.js +33 -0
- package/dist/lib/core-utils/timeout-guard-native.d.ts +7 -0
- package/dist/lib/core-utils/timeout-guard-native.js +40 -0
- package/dist/lib/core-utils/timeout-guard.d.ts +3 -3
- package/dist/lib/core-utils/timeout-guard.js +28 -13
- package/dist/lib/daemon/catalog.d.ts +18 -2
- package/dist/lib/daemon/catalog.js +18 -2
- package/dist/lib/daemon/config.d.ts +6 -0
- package/dist/lib/daemon/config.js +15 -2
- package/dist/lib/daemon/requests.d.ts +4 -0
- package/dist/lib/daemon/requests.js +2 -0
- package/dist/lib/daemon/run.js +94 -8
- package/dist/lib/daemon/supervisor.d.ts +8 -0
- package/dist/lib/daemon/supervisor.js +101 -1
- package/dist/lib/daemon/sync-runner.d.ts +58 -0
- package/dist/lib/daemon/sync-runner.js +277 -0
- package/dist/lib/doctor/__testing__/run-doctor.d.ts +8 -0
- package/dist/lib/doctor/__testing__/run-doctor.js +52 -0
- package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
- package/dist/lib/flag-registry-worker-source.generated.js +1 -1
- package/dist/lib/flag-registry.d.ts +12 -7
- package/dist/lib/flag-registry.js +66 -75
- package/dist/lib/flag-snapshot-file.d.ts +20 -0
- package/dist/lib/flag-snapshot-file.js +91 -0
- package/dist/lib/lanes/claude-balance.d.ts +6 -1
- package/dist/lib/lanes/claude-balance.js +46 -8
- package/dist/lib/lanes/config.js +15 -0
- package/dist/lib/lanes/hire-grant-dispatch.d.ts +2 -0
- package/dist/lib/lanes/hire-grant-dispatch.js +41 -0
- package/dist/lib/lanes/index.d.ts +1 -1
- package/dist/lib/lanes/keep-alive.d.ts +3 -0
- package/dist/lib/lanes/keep-alive.js +3 -0
- package/dist/lib/lanes/outpost-dispatch.d.ts +4 -0
- package/dist/lib/lanes/outpost-dispatch.js +11 -2
- package/dist/lib/lanes/reconcile.js +20 -4
- package/dist/lib/lanes/spawn.js +17 -10
- package/dist/lib/lanes/store.js +45 -1
- package/dist/lib/lanes/types.d.ts +30 -1
- package/dist/lib/lanes/types.js +2 -2
- package/dist/lib/lanes/watcher.js +3 -2
- package/dist/lib/process-flag-company-context.d.ts +8 -0
- package/dist/lib/process-flag-company-context.js +67 -0
- package/dist/main.js +3 -1
- package/dist/native-fast-route.d.ts +3 -0
- package/dist/native-fast-route.js +39 -0
- package/dist/secrets-env-fast-path.d.ts +5 -0
- package/dist/secrets-env-fast-path.js +16 -0
- package/dist/secrets-env-fast-route.d.ts +18 -0
- package/dist/secrets-env-fast-route.js +54 -0
- package/dist/utils/cli-telemetry.d.ts +26 -2
- package/dist/utils/cli-telemetry.js +178 -3
- package/dist/utils/hq-root-resolver.d.ts +13 -0
- package/dist/utils/hq-root-resolver.js +103 -0
- package/dist/utils/scaffold-fast-import-guard.js +6 -1
- package/dist/utils/vault-api.d.ts +8 -0
- package/dist/utils/vault-api.js +13 -1
- package/package.json +2 -2
|
@@ -1812,7 +1812,7 @@ function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, flagByCom
|
|
|
1812
1812
|
const rechecked = deps.readKeepAliveRecheckLane?.(hqRoot, lane.lane_id) ?? readLane(hqRoot, lane.lane_id);
|
|
1813
1813
|
// Dry-run backfills only in memory. Carry that simulated legacy timestamp
|
|
1814
1814
|
// through the fresh read when the stored lane version has not changed.
|
|
1815
|
-
|
|
1815
|
+
let current = deps.dryRun &&
|
|
1816
1816
|
initialState === "interrupted" &&
|
|
1817
1817
|
originalLane.interrupted_at === undefined &&
|
|
1818
1818
|
lane.interrupted_at !== undefined &&
|
|
@@ -1907,6 +1907,19 @@ function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, flagByCom
|
|
|
1907
1907
|
recordKeepAliveSkip(hqRoot, current, currentExclusion, summary, deps);
|
|
1908
1908
|
continue;
|
|
1909
1909
|
}
|
|
1910
|
+
if (current.keep_alive_blocked_reason === "superseded") {
|
|
1911
|
+
// The fresh exclusion check found no active successor. Clear the stale
|
|
1912
|
+
// reason before any later exit (backoff, queue full, retry cap, hire
|
|
1913
|
+
// refusal) so the watcher keeps treating the lane as resumable.
|
|
1914
|
+
const cleared = casLaneIf(hqRoot, current.lane_id, (latestLane) => latestLane.version === current.version &&
|
|
1915
|
+
latestLane.state === current.state &&
|
|
1916
|
+
latestLane.keep_alive === true &&
|
|
1917
|
+
latestLane.keep_alive_opted_in_at === current.keep_alive_opted_in_at &&
|
|
1918
|
+
latestLane.keep_alive_blocked_reason === "superseded", (latestLane) => ({ ...latestLane, keep_alive_blocked_reason: undefined }), deps.dryRun);
|
|
1919
|
+
if (!cleared.changed)
|
|
1920
|
+
continue;
|
|
1921
|
+
current = cleared.record;
|
|
1922
|
+
}
|
|
1910
1923
|
const currentSessionId = current.provider_session_id ?? current.last_resume?.session_id;
|
|
1911
1924
|
if (!providerSupportsSessionResume(current.provider) ||
|
|
1912
1925
|
typeof currentSessionId !== "string" ||
|
|
@@ -1934,9 +1947,12 @@ function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, flagByCom
|
|
|
1934
1947
|
continue;
|
|
1935
1948
|
}
|
|
1936
1949
|
const attempt = attempts + 1;
|
|
1950
|
+
// A done turn ends no later than its done envelope. Bookkeeping writes
|
|
1951
|
+
// (such as clearing a stale block) move updated_at but not the envelope,
|
|
1952
|
+
// so they cannot push the retry clock forward.
|
|
1937
1953
|
const terminalAtMs = initialState === "done"
|
|
1938
|
-
? Date.parse(current.updated_at)
|
|
1939
|
-
:
|
|
1954
|
+
? Math.min(Date.parse(current.updated_at), currentEligibilityAtMs)
|
|
1955
|
+
: currentEligibilityAtMs;
|
|
1940
1956
|
const attemptEligibleMs = terminalAtMs + leadLaneKeepAliveBackoffMs(attempt);
|
|
1941
1957
|
const storedNextMs = resetByHuman
|
|
1942
1958
|
? Number.NaN
|
|
@@ -1967,7 +1983,7 @@ function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, flagByCom
|
|
|
1967
1983
|
});
|
|
1968
1984
|
continue;
|
|
1969
1985
|
}
|
|
1970
|
-
const resumedFrom = current.state;
|
|
1986
|
+
const resumedFrom = current.state === "done" ? "done" : "interrupted";
|
|
1971
1987
|
const resumeText = resumedFrom === "interrupted"
|
|
1972
1988
|
? LEAD_LANE_KEEP_ALIVE_INTERRUPTED_RESUME_TEXT
|
|
1973
1989
|
: LEAD_LANE_KEEP_ALIVE_RESUME_TEXT;
|
package/dist/lib/lanes/spawn.js
CHANGED
|
@@ -1726,9 +1726,11 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
1726
1726
|
const recordedModel = repairRecordedAccount && existing.model_source === "account"
|
|
1727
1727
|
? existing.model
|
|
1728
1728
|
: undefined;
|
|
1729
|
-
|
|
1729
|
+
// `hq lanes set` on a running lane leaves the change pending until this spawn.
|
|
1730
|
+
const pendingSettings = existing.pending_settings;
|
|
1731
|
+
let pinnedModel = pendingSettings?.model ?? (existing.model_source === undefined || existing.model_source === "cli" || existing.model_source === "preserved"
|
|
1730
1732
|
? existing.model
|
|
1731
|
-
: undefined;
|
|
1733
|
+
: undefined);
|
|
1732
1734
|
const accountDeps = {
|
|
1733
1735
|
probe: modelDeps.probe ?? defaultProbe,
|
|
1734
1736
|
env,
|
|
@@ -1860,13 +1862,15 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
1860
1862
|
providerModel: config.providers[lane.provider].model,
|
|
1861
1863
|
});
|
|
1862
1864
|
const pendingResumeEffort = pendingResume !== undefined
|
|
1863
|
-
?
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1865
|
+
? pendingSettings?.effort !== undefined
|
|
1866
|
+
? { effort: pendingSettings.effort, source: "cli" }
|
|
1867
|
+
: {
|
|
1868
|
+
...(existing.effort !== undefined ? { effort: existing.effort } : {}),
|
|
1869
|
+
source: existing.effort_source ?? "none",
|
|
1870
|
+
}
|
|
1867
1871
|
: undefined;
|
|
1868
1872
|
const effort = launch.effort ?? pendingResumeEffort ?? resolveLaneEffort({
|
|
1869
|
-
cliEffort: existing.effort_source === "cli" ? existing.effort : undefined,
|
|
1873
|
+
cliEffort: pendingSettings?.effort ?? (existing.effort_source === "cli" ? existing.effort : undefined),
|
|
1870
1874
|
profileEffort,
|
|
1871
1875
|
accountEffort: accountCfg?.effort,
|
|
1872
1876
|
providerEffort: config.providers[lane.provider].effort,
|
|
@@ -2026,15 +2030,18 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
2026
2030
|
terminal_details: undefined,
|
|
2027
2031
|
// An account move's reservation ends with the claim.
|
|
2028
2032
|
pending_resume: undefined,
|
|
2033
|
+
pending_settings: undefined,
|
|
2029
2034
|
account: resolved.id,
|
|
2030
2035
|
provider_home_at_spawn: resolved.home,
|
|
2031
2036
|
...(model !== undefined ? { model } : {}),
|
|
2032
2037
|
...(model !== undefined
|
|
2033
2038
|
? {
|
|
2034
2039
|
model_source: pinnedModel !== undefined
|
|
2035
|
-
? preservedAccountModel
|
|
2036
|
-
? "
|
|
2037
|
-
: "
|
|
2040
|
+
? preservedAccountModel === undefined && pendingSettings?.model !== undefined
|
|
2041
|
+
? "cli"
|
|
2042
|
+
: preservedAccountModel !== undefined || existing.model_source === "preserved"
|
|
2043
|
+
? "preserved"
|
|
2044
|
+
: "cli"
|
|
2038
2045
|
: profileModel !== undefined
|
|
2039
2046
|
? "profile"
|
|
2040
2047
|
: accountCfg?.model !== undefined
|
package/dist/lib/lanes/store.js
CHANGED
|
@@ -475,6 +475,7 @@ export function parseLaneRecord(raw, filePath) {
|
|
|
475
475
|
"keep_alive_next_eligible_at",
|
|
476
476
|
"keep_alive_exhausted_at",
|
|
477
477
|
"interrupted_at",
|
|
478
|
+
"last_run_usage_at",
|
|
478
479
|
]) {
|
|
479
480
|
if (raw[field] !== undefined)
|
|
480
481
|
requireTimestamp(raw[field], field, filePath);
|
|
@@ -694,6 +695,40 @@ export function parseLaneRecord(raw, filePath) {
|
|
|
694
695
|
requireTimestamp(entry.at, `${field}.at`, filePath);
|
|
695
696
|
}
|
|
696
697
|
}
|
|
698
|
+
if (raw.settings_history !== undefined) {
|
|
699
|
+
if (!Array.isArray(raw.settings_history)) {
|
|
700
|
+
throw new LaneSchemaError("settings_history must be an array when present", {
|
|
701
|
+
path: filePath,
|
|
702
|
+
field: "settings_history",
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
for (const [index, entry] of raw.settings_history.entries()) {
|
|
706
|
+
const field = `settings_history[${index}]`;
|
|
707
|
+
if (!isPlainObject(entry) ||
|
|
708
|
+
Object.keys(entry).some((key) => !["at", "actor", "model", "effort", "reason"].includes(key)) ||
|
|
709
|
+
(entry.model === undefined && entry.effort === undefined)) {
|
|
710
|
+
throw new LaneSchemaError(`${field} must be a settings history entry`, { path: filePath, field });
|
|
711
|
+
}
|
|
712
|
+
requireTimestamp(entry.at, `${field}.at`, filePath);
|
|
713
|
+
if (typeof entry.actor !== "string" || entry.actor.trim() === "") {
|
|
714
|
+
throw new LaneSchemaError(`${field}.actor must be a non-empty string`, { path: filePath, field });
|
|
715
|
+
}
|
|
716
|
+
if (entry.reason !== undefined && (typeof entry.reason !== "string" || entry.reason.trim() === "")) {
|
|
717
|
+
throw new LaneSchemaError(`${field}.reason must be a non-empty string when present`, { path: filePath, field });
|
|
718
|
+
}
|
|
719
|
+
for (const key of ["model", "effort"]) {
|
|
720
|
+
const change = entry[key];
|
|
721
|
+
if (change === undefined)
|
|
722
|
+
continue;
|
|
723
|
+
if (!isPlainObject(change) ||
|
|
724
|
+
Object.keys(change).some((k) => k !== "from" && k !== "to") ||
|
|
725
|
+
typeof change.to !== "string" || change.to.trim() === "" ||
|
|
726
|
+
(change.from !== null && typeof change.from !== "string")) {
|
|
727
|
+
throw new LaneSchemaError(`${field}.${key} must be a {from, to} change`, { path: filePath, field });
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
697
732
|
if (raw.worker_profile_repin_history !== undefined) {
|
|
698
733
|
if (!Array.isArray(raw.worker_profile_repin_history)) {
|
|
699
734
|
throw new LaneSchemaError("worker_profile_repin_history must be an array when present", {
|
|
@@ -751,7 +786,7 @@ function parseLaneAccountSelection(value, filePath) {
|
|
|
751
786
|
const selection = value;
|
|
752
787
|
if (typeof selection.account !== "string" || selection.account.trim() === "")
|
|
753
788
|
throw failure("account");
|
|
754
|
-
if (selection.signal !== "usage" && selection.signal !== "lane_count" && selection.signal !== "explicit")
|
|
789
|
+
if (selection.signal !== "usage" && selection.signal !== "recent_usage" && selection.signal !== "lane_count" && selection.signal !== "explicit")
|
|
755
790
|
throw failure("signal");
|
|
756
791
|
if (selection.reason !== "least_loaded" && selection.reason !== "explicit")
|
|
757
792
|
throw failure("reason");
|
|
@@ -773,6 +808,10 @@ function parseLaneAccountSelection(value, filePath) {
|
|
|
773
808
|
throw failure(`considered.${index}`);
|
|
774
809
|
if (row.ineligible_reason !== undefined && typeof row.ineligible_reason !== "string")
|
|
775
810
|
throw failure(`considered.${index}.ineligible_reason`);
|
|
811
|
+
if (row.recent_usage_at !== undefined && row.recent_usage_at !== null &&
|
|
812
|
+
(typeof row.recent_usage_at !== "string" || !Number.isFinite(Date.parse(row.recent_usage_at)))) {
|
|
813
|
+
throw failure(`considered.${index}.recent_usage_at`);
|
|
814
|
+
}
|
|
776
815
|
if (row.usage !== null) {
|
|
777
816
|
if (!isPlainObject(row.usage))
|
|
778
817
|
throw failure(`considered.${index}.usage`);
|
|
@@ -1015,6 +1054,11 @@ export function mergeLaneRecords(live, sidecars) {
|
|
|
1015
1054
|
failed_resume_history: unionHistory(all.map((record) => record.failed_resume_history ?? []), entryIdentity, base.failed_resume_history ?? []),
|
|
1016
1055
|
}
|
|
1017
1056
|
: {}),
|
|
1057
|
+
...(all.some((record) => record.settings_history !== undefined)
|
|
1058
|
+
? {
|
|
1059
|
+
settings_history: unionHistory(all.map((record) => record.settings_history ?? []), entryIdentity, base.settings_history ?? []),
|
|
1060
|
+
}
|
|
1061
|
+
: {}),
|
|
1018
1062
|
...(all.some((record) => record.stop_history !== undefined)
|
|
1019
1063
|
? {
|
|
1020
1064
|
stop_history: unionHistory(all.map((record) => record.stop_history ?? []), entryIdentity, base.stop_history ?? []),
|
|
@@ -253,12 +253,14 @@ export interface LaneAccountSelectionConsidered {
|
|
|
253
253
|
weekly_percent?: number;
|
|
254
254
|
sampled_at?: string;
|
|
255
255
|
} | null;
|
|
256
|
+
/** Latest completed run in the recent-usage window, when lane snapshots are complete. */
|
|
257
|
+
recent_usage_at?: string | null;
|
|
256
258
|
hosts: LaneAccountSelectionHostCount[];
|
|
257
259
|
}
|
|
258
260
|
/** Decision evidence saved with a Claude lane when account balancing is active. */
|
|
259
261
|
export interface LaneAccountSelection {
|
|
260
262
|
account: string;
|
|
261
|
-
signal: "usage" | "lane_count" | "explicit";
|
|
263
|
+
signal: "usage" | "recent_usage" | "lane_count" | "explicit";
|
|
262
264
|
reason: "least_loaded" | "explicit";
|
|
263
265
|
target_host: LaneHost;
|
|
264
266
|
selected_at: string;
|
|
@@ -287,6 +289,20 @@ export interface LaneStopHistoryEntry {
|
|
|
287
289
|
at: string;
|
|
288
290
|
reason: string;
|
|
289
291
|
}
|
|
292
|
+
/** One `hq lanes set` change; it takes effect when the lane next resumes. */
|
|
293
|
+
export interface LaneSettingsHistoryEntry {
|
|
294
|
+
at: string;
|
|
295
|
+
actor: string;
|
|
296
|
+
model?: {
|
|
297
|
+
from: string | null;
|
|
298
|
+
to: string;
|
|
299
|
+
};
|
|
300
|
+
effort?: {
|
|
301
|
+
from: string | null;
|
|
302
|
+
to: string;
|
|
303
|
+
};
|
|
304
|
+
reason?: string;
|
|
305
|
+
}
|
|
290
306
|
/** Evidence for a senior-authorized Outpost worker-profile content re-pin. */
|
|
291
307
|
export interface WorkerProfileRepinHistoryEntry {
|
|
292
308
|
old_hash: string | null;
|
|
@@ -564,6 +580,17 @@ export interface LaneRecord {
|
|
|
564
580
|
terminal_reason?: string;
|
|
565
581
|
/** Reasons supplied to explicit stop commands, retained independently of optional project ledgers. */
|
|
566
582
|
stop_history?: LaneStopHistoryEntry[];
|
|
583
|
+
/** Model and effort changes made with `hq lanes set`. */
|
|
584
|
+
settings_history?: LaneSettingsHistoryEntry[];
|
|
585
|
+
/**
|
|
586
|
+
* Model or effort from `hq lanes set` on a running lane. The running turn
|
|
587
|
+
* keeps its launched values (quota classification reads `model`); the next
|
|
588
|
+
* spawn applies these and clears them.
|
|
589
|
+
*/
|
|
590
|
+
pending_settings?: {
|
|
591
|
+
model?: string;
|
|
592
|
+
effort?: string;
|
|
593
|
+
};
|
|
567
594
|
/** Classification time when this lane entered interrupted; stable across later metadata writes. */
|
|
568
595
|
interrupted_at?: string;
|
|
569
596
|
/**
|
|
@@ -598,6 +625,8 @@ export interface LaneRecord {
|
|
|
598
625
|
last_resume?: LaneResumeRecord;
|
|
599
626
|
/** Cache counters from the last exited run's result. Nothing else from the payload. */
|
|
600
627
|
last_run_usage?: LaneRunUsage;
|
|
628
|
+
/** Completion time paired with last_run_usage; written by the watcher. */
|
|
629
|
+
last_run_usage_at?: string;
|
|
601
630
|
/**
|
|
602
631
|
* Spec §4: one repair turn per expected envelope slot when the final output
|
|
603
632
|
* is invalid or missing. Written before repair spawn and updated on repair
|
package/dist/lib/lanes/types.js
CHANGED
|
@@ -230,9 +230,9 @@ export const LANE_FIELD_WRITERS = {
|
|
|
230
230
|
last_envelope: ["watcher", "reconcile"], model: ["creator", "spawn", "command"], model_source: ["creator", "spawn", "command"], effort: ["creator", "spawn", "command"], effort_source: ["creator", "spawn", "command"], fast_mode: ["spawn"],
|
|
231
231
|
last_message_at: ["command"], status_gate: ["spawn", "watcher"], status_gate_history: ["watcher"], blocker_freshness_state: ["watcher"], kr_freshness_state: ["watcher"], last_compaction: ["spawn"], story_complete: ["watcher", "reconcile"], story_complete_reason: ["watcher", "reconcile"], story_status: ["watcher", "reconcile"],
|
|
232
232
|
overdue: ["reconcile"], overdue_at: ["reconcile"], last_host_report_at: ["watcher", "spawn", "reconcile"], stalled: ["watcher"], stalled_at: ["watcher"], stalled_reason: ["watcher"],
|
|
233
|
-
admitted_reason: ["command", "reconcile"], terminal_reason: ["command", "watcher", "spawn", "reconcile"], stop_history: ["command"], interrupted_at: ["watcher", "reconcile"], terminal_details: ["command", "spawn", "watcher", "reconcile"],
|
|
233
|
+
admitted_reason: ["command", "reconcile"], terminal_reason: ["command", "watcher", "spawn", "reconcile"], stop_history: ["command"], settings_history: ["command"], pending_settings: ["command", "spawn"], interrupted_at: ["command", "watcher", "reconcile"], terminal_details: ["command", "spawn", "watcher", "reconcile"],
|
|
234
234
|
quota_reset_at: ["watcher", "reconcile"], model_quota_marker: ["watcher", "reconcile"], outpost_command_id: ["command"],
|
|
235
|
-
provider_session_id: ["watcher", "command", "spawn"], provider_session_id_absent: ["watcher", "command"], provider_home_at_spawn: ["spawn"], last_resume: ["spawn"], last_run_usage: ["watcher"], envelope_repair: ["watcher", "reconcile"], envelope_validation_errors: ["watcher"], last_progress_event_at: ["watcher"],
|
|
235
|
+
provider_session_id: ["watcher", "command", "spawn"], provider_session_id_absent: ["watcher", "command"], provider_home_at_spawn: ["spawn"], last_resume: ["spawn"], last_run_usage: ["watcher"], last_run_usage_at: ["watcher"], envelope_repair: ["watcher", "reconcile"], envelope_validation_errors: ["watcher"], last_progress_event_at: ["watcher"],
|
|
236
236
|
};
|
|
237
237
|
function isPlainObject(value) {
|
|
238
238
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -245,7 +245,7 @@ export function notifySlotFreed(_input) {
|
|
|
245
245
|
* or unparseable id is recorded as absent with its reason, never invented.
|
|
246
246
|
* Non-resumable providers return null and the record is left as it was.
|
|
247
247
|
*/
|
|
248
|
-
function runResultFields(hqRoot, lane) {
|
|
248
|
+
function runResultFields(hqRoot, lane, at) {
|
|
249
249
|
const runDir = laneRunDir(hqRoot, lane.lane_id);
|
|
250
250
|
const result = readWorkerRunResult(runDir, lane.provider, readSpawnManifest(runDir).worker_out_offset ?? 0);
|
|
251
251
|
if (result === null)
|
|
@@ -255,6 +255,7 @@ function runResultFields(hqRoot, lane) {
|
|
|
255
255
|
cache_read_input_tokens: result.cache_read_input_tokens,
|
|
256
256
|
cache_creation_input_tokens: result.cache_creation_input_tokens,
|
|
257
257
|
},
|
|
258
|
+
last_run_usage_at: at,
|
|
258
259
|
};
|
|
259
260
|
if (result.session_id !== null) {
|
|
260
261
|
fields.provider_session_id = result.session_id;
|
|
@@ -366,7 +367,7 @@ export function applyTerminalToLaneWithResult(hqRoot, laneId, classification, op
|
|
|
366
367
|
}
|
|
367
368
|
const next = {
|
|
368
369
|
...current,
|
|
369
|
-
...runResultFields(hqRoot, current),
|
|
370
|
+
...runResultFields(hqRoot, current, at),
|
|
370
371
|
state: effectiveClassification.state,
|
|
371
372
|
queued_reason: undefined,
|
|
372
373
|
stalled: undefined,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Company scope shared by the full flag registry and its fast-path reader. */
|
|
2
|
+
export interface ProcessFlagCompanyContext {
|
|
3
|
+
companyUid?: string;
|
|
4
|
+
companySlug?: string;
|
|
5
|
+
}
|
|
6
|
+
/** Resolve the same company identity the process-wide flag reader uses. */
|
|
7
|
+
export declare function resolveProcessFlagCompanyContext(env?: NodeJS.ProcessEnv): ProcessFlagCompanyContext;
|
|
8
|
+
//# sourceMappingURL=process-flag-company-context.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Company scope shared by the full flag registry and its fast-path reader. */
|
|
2
|
+
import { readMachineCompanyBinding } from "./work-context/company.js";
|
|
3
|
+
import { readSessionState } from "./work-context/state.js";
|
|
4
|
+
import { workContextRoot } from "./work-context/paths.js";
|
|
5
|
+
import { readCompaniesManifestMap } from "./work-context/repo-remote.js";
|
|
6
|
+
import { resolveDefaultHqRoot } from "../utils/hq-root-resolver.js";
|
|
7
|
+
const COMPANY_UID_RE = /^cmp_[A-Za-z0-9]{3,128}$/;
|
|
8
|
+
const COMPANY_SLUG_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
9
|
+
function normalizedCompanyUid(value) {
|
|
10
|
+
const candidate = value?.trim() ?? "";
|
|
11
|
+
return COMPANY_UID_RE.test(candidate) ? candidate : undefined;
|
|
12
|
+
}
|
|
13
|
+
function normalizedCompanySlug(value) {
|
|
14
|
+
const candidate = value?.trim() ?? "";
|
|
15
|
+
return COMPANY_SLUG_RE.test(candidate) ? candidate : undefined;
|
|
16
|
+
}
|
|
17
|
+
/** Resolve explicit, machine, or bound-session company scope without defaults. */
|
|
18
|
+
function processCompanyContext(env = process.env) {
|
|
19
|
+
const explicitUid = normalizedCompanyUid(env.HQ_COMPANY_UID);
|
|
20
|
+
const explicitSlug = normalizedCompanySlug(env.HQ_COMPANY_SLUG);
|
|
21
|
+
if (explicitUid || explicitSlug) {
|
|
22
|
+
return { companyUid: explicitUid, companySlug: explicitSlug };
|
|
23
|
+
}
|
|
24
|
+
const machine = readMachineCompanyBinding(env);
|
|
25
|
+
const spawnValue = env.HQ_SPAWN_COMPANY?.trim();
|
|
26
|
+
const spawnSlug = normalizedCompanySlug(spawnValue);
|
|
27
|
+
const machineContext = spawnValue && COMPANY_UID_RE.test(spawnValue)
|
|
28
|
+
? { companyUid: spawnValue }
|
|
29
|
+
: spawnSlug
|
|
30
|
+
? { companySlug: spawnSlug }
|
|
31
|
+
: {
|
|
32
|
+
companyUid: normalizedCompanyUid(machine?.uid),
|
|
33
|
+
companySlug: normalizedCompanySlug(machine?.slug),
|
|
34
|
+
};
|
|
35
|
+
if (machineContext.companyUid || machineContext.companySlug)
|
|
36
|
+
return machineContext;
|
|
37
|
+
const sessionId = env.HQ_SESSION_ID?.trim();
|
|
38
|
+
if (!sessionId)
|
|
39
|
+
return {};
|
|
40
|
+
const session = readSessionState(sessionId, workContextRoot(undefined, env));
|
|
41
|
+
if (session?.contextStatus !== "bound" || session.companyConfidence === "device_default") {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
companyUid: normalizedCompanyUid(session.companyUid),
|
|
46
|
+
companySlug: normalizedCompanySlug(session.companySlug),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function companyUidForContext(context, env = process.env) {
|
|
50
|
+
if (context.companyUid)
|
|
51
|
+
return context.companyUid;
|
|
52
|
+
if (!context.companySlug)
|
|
53
|
+
return undefined;
|
|
54
|
+
const hqRoot = resolveDefaultHqRoot({ env });
|
|
55
|
+
const manifestUid = readCompaniesManifestMap(hqRoot)?.[context.companySlug]?.cloud_uid;
|
|
56
|
+
return typeof manifestUid === "string" ? normalizedCompanyUid(manifestUid) : undefined;
|
|
57
|
+
}
|
|
58
|
+
/** Resolve the same company identity the process-wide flag reader uses. */
|
|
59
|
+
export function resolveProcessFlagCompanyContext(env = process.env) {
|
|
60
|
+
const context = processCompanyContext(env);
|
|
61
|
+
const companyUid = companyUidForContext(context, env);
|
|
62
|
+
return {
|
|
63
|
+
...(companyUid ? { companyUid } : {}),
|
|
64
|
+
...(context.companySlug ? { companySlug: context.companySlug } : {}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=process-flag-company-context.js.map
|
package/dist/main.js
CHANGED
|
@@ -43,7 +43,7 @@ import { enforceVersionGate, shouldSkipGate, } from "./utils/version-gate.js";
|
|
|
43
43
|
import { autoUpdateAndReexec } from "./utils/self-update.js";
|
|
44
44
|
import { CLI_VERSION } from "./cli-version.js";
|
|
45
45
|
import { findLazyCommand, registerCommandCatalog } from "./lazy-commands.js";
|
|
46
|
-
import { emitCliFirstSyncNextStepOutcome, emitCliSessionStarted, takeFirstSyncStepOutcomes, maybeShowFirstSyncNextStep, } from "./utils/cli-telemetry.js";
|
|
46
|
+
import { emitCliFirstSyncNextStepOutcome, emitCliSessionStarted, emitPlanLimitInviteTeammateExposure, takeFirstSyncStepOutcomes, takePlanLimitInviteTeammateExposures, maybeShowFirstSyncNextStep, } from "./utils/cli-telemetry.js";
|
|
47
47
|
import { reportCliClientHealthInvocation } from "./utils/client-health.js";
|
|
48
48
|
import { settleWithin } from "./utils/settle-with-timeout.js";
|
|
49
49
|
import { emitPlanLimitNag } from "./lib/plan-limit-nag.js";
|
|
@@ -294,10 +294,12 @@ export async function runCli() {
|
|
|
294
294
|
// slice even when the command itself was long-running.
|
|
295
295
|
const releaseHealthTimeoutMs = foregroundNetworkBudget.timeoutFor(FINALIZATION_FOREGROUND_WAIT_MS);
|
|
296
296
|
const firstSyncOutcomeFlushes = takeFirstSyncStepOutcomes().map((outcome) => emitCliFirstSyncNextStepOutcome(outcome, releaseHealthTimeoutMs));
|
|
297
|
+
const inviteTeammateExposureFlushes = takePlanLimitInviteTeammateExposures().map((companyUid) => emitPlanLimitInviteTeammateExposure(companyUid, releaseHealthTimeoutMs));
|
|
297
298
|
await settleWithin([
|
|
298
299
|
refreshVersionCache({ timeoutMs: releaseHealthTimeoutMs }),
|
|
299
300
|
Sentry.flush(releaseHealthTimeoutMs),
|
|
300
301
|
...firstSyncOutcomeFlushes,
|
|
302
|
+
...inviteTeammateExposureFlushes,
|
|
301
303
|
], releaseHealthTimeoutMs);
|
|
302
304
|
});
|
|
303
305
|
// Last, so it wins over anything the (skipped) command path would have
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Default-off flag check for native entrypoint routes. */
|
|
2
|
+
import { localOverrideEnvVar, readLocalOverride } from "@indigoai-us/hq-flags-client";
|
|
3
|
+
import { parseFastCore } from "./fast-scaffold.js";
|
|
4
|
+
export async function nativeFastRouteEnabled(argv, env = process.env) {
|
|
5
|
+
const parsed = parseFastCore(argv);
|
|
6
|
+
if (!parsed || parsed.entry.kind !== "native")
|
|
7
|
+
return false;
|
|
8
|
+
const override = nativeRouteOverride(parsed.entry.flagKey, env);
|
|
9
|
+
if (override !== null)
|
|
10
|
+
return override;
|
|
11
|
+
try {
|
|
12
|
+
const [{ resolveProcessFlagCompanyContext }, { readFlagSnapshotFile }] = await Promise.all([
|
|
13
|
+
import("./lib/process-flag-company-context.js"),
|
|
14
|
+
import("./lib/flag-snapshot-file.js"),
|
|
15
|
+
]);
|
|
16
|
+
const { companyUid } = resolveProcessFlagCompanyContext(env);
|
|
17
|
+
if (!companyUid)
|
|
18
|
+
return false;
|
|
19
|
+
return readFlagSnapshotFile(companyUid, { env })?.flags[parsed.entry.flagKey] === true;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// The new path is a rollout optimization. Any unavailable local scope or
|
|
23
|
+
// cache data leaves the established full-graph route in control.
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** The route's documented CLI override also accepts the conventional 1/0. */
|
|
28
|
+
function nativeRouteOverride(flagKey, env) {
|
|
29
|
+
const override = readLocalOverride(flagKey, env);
|
|
30
|
+
if (override !== null)
|
|
31
|
+
return override;
|
|
32
|
+
const raw = env[localOverrideEnvVar(flagKey)];
|
|
33
|
+
if (raw === "1")
|
|
34
|
+
return true;
|
|
35
|
+
if (raw === "0")
|
|
36
|
+
return false;
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=native-fast-route.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ParsedSecretsEnvFastRequest } from "./fast-scaffold.js";
|
|
2
|
+
import type { SecretsEnvFastResult } from "./secrets-env-fast-route.js";
|
|
3
|
+
/** Run the shared secret action without importing the full CLI command graph. */
|
|
4
|
+
export declare function runSecretsEnvFastPath(request: ParsedSecretsEnvFastRequest): Promise<SecretsEnvFastResult>;
|
|
5
|
+
//# sourceMappingURL=secrets-env-fast-path.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Run the shared secret action without importing the full CLI command graph. */
|
|
2
|
+
export async function runSecretsEnvFastPath(request) {
|
|
3
|
+
if (process.env.HQ_AGENT_DIR?.trim()) {
|
|
4
|
+
const [os, { agentKitPaths }, { adoptAgentIdentityEnv }] = await Promise.all([
|
|
5
|
+
import("node:os"),
|
|
6
|
+
import("./lib/agent-kit/paths.js"),
|
|
7
|
+
import("./lib/agent-kit/adopt-identity.js"),
|
|
8
|
+
]);
|
|
9
|
+
adoptAgentIdentityEnv(agentKitPaths(os.homedir(), process.env), process.env);
|
|
10
|
+
}
|
|
11
|
+
const { runSecretsEnvCommand } = await import("./commands/secrets.js");
|
|
12
|
+
return runSecretsEnvCommand(request.options, request.scope, {
|
|
13
|
+
fallbackOnAuthFailure: true,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=secrets-env-fast-path.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Default-off hq-flags gate for the `secrets env` entry fast path. */
|
|
2
|
+
import type { ParsedSecretsEnvFastRequest } from "./fast-scaffold.js";
|
|
3
|
+
export declare const CLI_SECRETS_ENV_FAST_PATH_FLAG = "cli.secrets-env-fast-path";
|
|
4
|
+
export type SecretsEnvFastResult = "handled" | "fallback";
|
|
5
|
+
export interface SecretsEnvFastDispatchDeps {
|
|
6
|
+
isEnabled: () => Promise<boolean>;
|
|
7
|
+
runFast: () => Promise<SecretsEnvFastResult>;
|
|
8
|
+
runCurrent: () => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
/** Keep the existing command graph in control when the gate is off or declines. */
|
|
11
|
+
export declare function dispatchSecretsEnvFastRequest(_request: ParsedSecretsEnvFastRequest, deps: SecretsEnvFastDispatchDeps): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Fast routes only use an explicit local override or the latest company flag
|
|
14
|
+
* snapshot. A missing company, stale snapshot, or unreadable cache leaves the
|
|
15
|
+
* current CLI path in control; it never performs a registry network request.
|
|
16
|
+
*/
|
|
17
|
+
export declare function secretsEnvFastRouteEnabled(request: ParsedSecretsEnvFastRequest, env?: NodeJS.ProcessEnv): Promise<boolean>;
|
|
18
|
+
//# sourceMappingURL=secrets-env-fast-route.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Default-off hq-flags gate for the `secrets env` entry fast path. */
|
|
2
|
+
import { localOverrideEnvVar, readLocalOverride } from "@indigoai-us/hq-flags-client";
|
|
3
|
+
export const CLI_SECRETS_ENV_FAST_PATH_FLAG = "cli.secrets-env-fast-path";
|
|
4
|
+
/** Keep the existing command graph in control when the gate is off or declines. */
|
|
5
|
+
export async function dispatchSecretsEnvFastRequest(_request, deps) {
|
|
6
|
+
if (!(await deps.isEnabled())) {
|
|
7
|
+
await deps.runCurrent();
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if ((await deps.runFast()) === "fallback") {
|
|
11
|
+
await deps.runCurrent();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Fast routes only use an explicit local override or the latest company flag
|
|
16
|
+
* snapshot. A missing company, stale snapshot, or unreadable cache leaves the
|
|
17
|
+
* current CLI path in control; it never performs a registry network request.
|
|
18
|
+
*/
|
|
19
|
+
export async function secretsEnvFastRouteEnabled(request, env = process.env) {
|
|
20
|
+
const override = readLocalOverride(CLI_SECRETS_ENV_FAST_PATH_FLAG, env);
|
|
21
|
+
if (override !== null)
|
|
22
|
+
return override;
|
|
23
|
+
const rawOverride = env[localOverrideEnvVar(CLI_SECRETS_ENV_FAST_PATH_FLAG)];
|
|
24
|
+
if (rawOverride === "1")
|
|
25
|
+
return true;
|
|
26
|
+
if (rawOverride === "0")
|
|
27
|
+
return false;
|
|
28
|
+
if (request.scope.personal)
|
|
29
|
+
return false;
|
|
30
|
+
try {
|
|
31
|
+
const scopedEnv = { ...env };
|
|
32
|
+
if (request.scope.company) {
|
|
33
|
+
// An explicit --company scope must override any ambient binding for the
|
|
34
|
+
// fast-path flag lookup, just as it overrides the secrets command scope.
|
|
35
|
+
delete scopedEnv.HQ_COMPANY_UID;
|
|
36
|
+
scopedEnv.HQ_COMPANY_SLUG = request.scope.company;
|
|
37
|
+
}
|
|
38
|
+
const [{ resolveProcessFlagCompanyContext }, { readFlagSnapshotFile }] = await Promise.all([
|
|
39
|
+
import("./lib/process-flag-company-context.js"),
|
|
40
|
+
import("./lib/flag-snapshot-file.js"),
|
|
41
|
+
]);
|
|
42
|
+
const { companyUid } = resolveProcessFlagCompanyContext(scopedEnv);
|
|
43
|
+
if (!companyUid)
|
|
44
|
+
return false;
|
|
45
|
+
return readFlagSnapshotFile(companyUid, { env: scopedEnv })?.flags[CLI_SECRETS_ENV_FAST_PATH_FLAG] === true;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
// This is a performance rollout gate. Failure to read it preserves the
|
|
49
|
+
// existing full CLI behavior.
|
|
50
|
+
console.warn("[hq] secrets env fast-path flag lookup failed; using the full CLI path", error instanceof Error ? error.name : typeof error);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=secrets-env-fast-route.js.map
|
|
@@ -3,16 +3,39 @@ export { CLI_FIRST_SYNC_NEXT_STEP_FLAG, FIRST_SYNC_CONFIRMATION_COPY, FIRST_SYNC
|
|
|
3
3
|
export declare const TELEMETRY_TIMEOUT_MS = 1200;
|
|
4
4
|
/**
|
|
5
5
|
* Exact stderr line printed once on the first signed-in TTY run that is not
|
|
6
|
-
* `--json`. Keep this notice and its marker stable for
|
|
7
|
-
*
|
|
6
|
+
* `--json`. Keep this notice and its marker stable for session telemetry;
|
|
7
|
+
* first-sync and feature-used fields have separate opt-in disclosures below.
|
|
8
8
|
*/
|
|
9
9
|
export declare const TELEMETRY_DISCLOSURE_LINE = "HQ sends a usage event when you run a signed-in command (CLI version and a timestamp, never command names, file contents, or secrets) to improve the CLI. Set HQ_TELEMETRY=0 to turn this off.";
|
|
10
10
|
export declare const FIRST_SYNC_TELEMETRY_DISCLOSURE_LINE = "HQ sends a usage event when you run a signed-in command (CLI version and a timestamp; when enabled, first-sync guidance shown or followed and the flag key cli.first-sync-next-step; never command names, file contents, or secrets) to improve the CLI. Set HQ_TELEMETRY=0 to turn this off.";
|
|
11
|
+
export declare const CLI_FEATURE_USED_TELEMETRY_FLAG = "cli.feature-used-telemetry-v1";
|
|
12
|
+
export declare const CLI_FEATURE_USED_TELEMETRY_DISCLOSURE_LINE = "HQ sends a feature-use event when you run a supported CLI feature (feature kind and outcome, plus your company UID when available; never command names, file contents, or secrets). The hq-flags key cli.feature-used-telemetry-v1 controls these events. Set HQ_TELEMETRY=0 to turn this off.";
|
|
13
|
+
/** Queue one exposure after the CLI has printed the invite suggestion. */
|
|
14
|
+
export declare function queuePlanLimitInviteTeammateExposure(companyUid: string): void;
|
|
15
|
+
/** Drain invite-suggestion exposures for the bounded command-finalization flush. */
|
|
16
|
+
export declare function takePlanLimitInviteTeammateExposures(): string[];
|
|
17
|
+
export type CliFeatureKind = "secret_read" | "lane_create" | "lane_resume";
|
|
18
|
+
export type CliFeatureOutcome = "success" | "failure";
|
|
19
|
+
export interface CliFeatureUsage {
|
|
20
|
+
kind: CliFeatureKind;
|
|
21
|
+
outcome: CliFeatureOutcome;
|
|
22
|
+
/** Only company UIDs are accepted; names and slugs are never telemetry fields. */
|
|
23
|
+
companyUid?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Emit one bounded, privacy-limited feature-use event with a cached identity.
|
|
27
|
+
* It never refreshes credentials or turns telemetry failure into command failure.
|
|
28
|
+
*/
|
|
29
|
+
export declare function emitCliFeatureUsed(usage: CliFeatureUsage, timeoutMs?: number): Promise<void>;
|
|
30
|
+
/** Run one action and emit exactly one success/failure event without masking errors. */
|
|
31
|
+
export declare function withCliFeatureUsed<T>(kind: CliFeatureKind, action: () => Promise<T>, companyUid?: string): Promise<T>;
|
|
11
32
|
/**
|
|
12
33
|
* `HQ_TELEMETRY=0` (also `false` and `off`, case-insensitive) disables every
|
|
13
34
|
* CLI POST to `/v1/telemetry/events`. `HQ_TELEMETRY_TIMEOUT_MS` is unchanged.
|
|
14
35
|
*/
|
|
15
36
|
export declare function isCliTelemetryDisabled(env?: NodeJS.ProcessEnv): boolean;
|
|
37
|
+
/** Feature-use collection stays off until the HQ flag is explicitly enabled. */
|
|
38
|
+
export declare function isCliFeatureUsedTelemetryEnabled(): boolean;
|
|
16
39
|
export declare function telemetryDisclosureMarkerPath(): string;
|
|
17
40
|
/**
|
|
18
41
|
* Print the one-line disclosure at most once per machine. Returns whether
|
|
@@ -31,4 +54,5 @@ export declare function maybeDiscloseCliTelemetry(stderr?: {
|
|
|
31
54
|
export declare function emitCliSessionStarted(timeoutMs?: number): Promise<void>;
|
|
32
55
|
/** Emit one authenticated measurement for a first-sync prompt outcome. */
|
|
33
56
|
export declare function emitCliFirstSyncNextStepOutcome(outcome: FirstSyncStepOutcome, timeoutMs?: number): Promise<void>;
|
|
57
|
+
export declare function emitPlanLimitInviteTeammateExposure(companyUid: string, timeoutMs?: number): Promise<void>;
|
|
34
58
|
//# sourceMappingURL=cli-telemetry.d.ts.map
|