@indigoai-us/hq-cli 5.266.0 → 5.268.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 +18 -0
- package/dist/lib/billing/plan-limit-upgrade-url.d.ts +3 -2
- package/dist/lib/billing/plan-limit-upgrade-url.js +50 -11
- package/dist/lib/bot/inbox-poll-backoff-flag.d.ts +1 -1
- package/dist/lib/bot/inbox-poll-backoff-flag.js +4 -1
- package/dist/lib/checkpoint-ontology/multi-writer.d.ts +1 -1
- package/dist/lib/checkpoint-ontology/multi-writer.js +3 -0
- package/dist/utils/plan-gate-error.js +9 -6
- package/dist/utils/team-upgrade.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.268.0] — 2026-09-28
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `hq core ontology duplicates` now proposes the original entity file as the
|
|
10
|
+
survivor when it ties with its numbered copies (`hq-core 2.md`). Before, the
|
|
11
|
+
path tiebreak picked the copy, which it did for 129 of 530 Indigo groups.
|
|
12
|
+
|
|
13
|
+
## [5.267.0] — 2026-09-28
|
|
14
|
+
|
|
15
|
+
- Company billing links opened by CLI plan-limit flows now carry cli_limit attribution into checkout telemetry.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Local bots now read the idle inbox backoff flag with a 2 s budget. The
|
|
20
|
+
150 ms budget timed out from home networks, so bots kept polling every 2 s
|
|
21
|
+
even when the flag was on for them.
|
|
22
|
+
|
|
5
23
|
## [5.266.0] — 2026-09-28
|
|
6
24
|
|
|
7
25
|
### Added
|
|
@@ -6,8 +6,9 @@ export interface PlanLimitUpgradeUrlChoices {
|
|
|
6
6
|
legacyUrl: string;
|
|
7
7
|
}
|
|
8
8
|
export declare function isPlanLimitUpgradeFallbackEnabled(): boolean;
|
|
9
|
-
/** Resolve
|
|
9
|
+
/** Resolve and attribute the final selected company billing destination. */
|
|
10
10
|
export declare function selectPlanLimitUpgradeUrl(companySlug: string | undefined, choices: PlanLimitUpgradeUrlChoices): string;
|
|
11
|
-
|
|
11
|
+
export declare function withCliLimitEntrySurface(value: string): string;
|
|
12
|
+
/** Keep the existing destination and flag behavior while labeling company limit links. */
|
|
12
13
|
export declare function planLimitUpgradeFallbackUrl(companySlug: string | undefined, previousFallbackUrl: string): string;
|
|
13
14
|
//# sourceMappingURL=plan-limit-upgrade-url.d.ts.map
|
|
@@ -13,23 +13,62 @@ function companyBillingUrl(companySlug) {
|
|
|
13
13
|
return null;
|
|
14
14
|
const slug = companySlug?.trim();
|
|
15
15
|
const route = slug
|
|
16
|
-
? `/companies/${encodeURIComponent(slug)}/billing?upgrade=1`
|
|
16
|
+
? `/companies/${encodeURIComponent(slug)}/billing?upgrade=1&entrySurface=cli_limit`
|
|
17
17
|
: "/billing";
|
|
18
18
|
return `${origin}${route}`;
|
|
19
19
|
}
|
|
20
|
-
/** Resolve
|
|
20
|
+
/** Resolve and attribute the final selected company billing destination. */
|
|
21
21
|
export function selectPlanLimitUpgradeUrl(companySlug, choices) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const nestedUrl = choices.nestedUrl?.trim() || undefined;
|
|
26
|
-
const generatedUrl = companyBillingUrl(companySlug);
|
|
27
|
-
if (companySlug?.trim()) {
|
|
28
|
-
return generatedUrl ?? topLevelUrl ?? nestedUrl ?? choices.legacyUrl;
|
|
22
|
+
let selected;
|
|
23
|
+
if (!isPlanLimitUpgradeFallbackEnabled()) {
|
|
24
|
+
selected = choices.legacyUrl;
|
|
29
25
|
}
|
|
30
|
-
|
|
26
|
+
else {
|
|
27
|
+
const topLevelUrl = choices.topLevelUrl?.trim() || undefined;
|
|
28
|
+
const nestedUrl = choices.nestedUrl?.trim() || undefined;
|
|
29
|
+
const generatedUrl = companyBillingUrl(companySlug);
|
|
30
|
+
if (companySlug?.trim()) {
|
|
31
|
+
selected = generatedUrl ?? topLevelUrl ?? nestedUrl ?? choices.legacyUrl;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
selected = topLevelUrl ?? nestedUrl ?? generatedUrl ?? choices.legacyUrl;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return withCliLimitEntrySurface(selected);
|
|
38
|
+
}
|
|
39
|
+
export function withCliLimitEntrySurface(value) {
|
|
40
|
+
try {
|
|
41
|
+
const url = new URL(value);
|
|
42
|
+
if (url.protocol !== "https:" || url.username || url.password)
|
|
43
|
+
return value;
|
|
44
|
+
const callbackUrl = url.searchParams.get("callbackUrl");
|
|
45
|
+
if (callbackUrl) {
|
|
46
|
+
const callback = new URL(callbackUrl, url.origin);
|
|
47
|
+
const parts = callback.pathname.split("/").filter(Boolean);
|
|
48
|
+
if (parts.length === 5 && parts[0] === "api" && parts[1] === "companies" &&
|
|
49
|
+
/^cmp_[A-Za-z0-9_-]+$/.test(parts[2]) && parts[3] === "billing" && parts[4] === "upgrade") {
|
|
50
|
+
callback.searchParams.set("entrySurface", "cli_limit");
|
|
51
|
+
const serializedCallback = /^[a-z][a-z0-9+.-]*:/i.test(callbackUrl)
|
|
52
|
+
? callback.toString()
|
|
53
|
+
: callbackUrl.startsWith("//")
|
|
54
|
+
? `//${callback.host}${callback.pathname}${callback.search}${callback.hash}`
|
|
55
|
+
: `${callback.pathname}${callback.search}${callback.hash}`;
|
|
56
|
+
url.searchParams.set("callbackUrl", serializedCallback);
|
|
57
|
+
return url.toString();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
61
|
+
if (parts.length === 3 && parts[0] === "companies" && parts[2] === "billing") {
|
|
62
|
+
url.searchParams.set("entrySurface", "cli_limit");
|
|
63
|
+
return url.toString();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
return value;
|
|
31
70
|
}
|
|
32
|
-
/** Keep
|
|
71
|
+
/** Keep the existing destination and flag behavior while labeling company limit links. */
|
|
33
72
|
export function planLimitUpgradeFallbackUrl(companySlug, previousFallbackUrl) {
|
|
34
73
|
return selectPlanLimitUpgradeUrl(companySlug, {
|
|
35
74
|
legacyUrl: previousFallbackUrl,
|
|
@@ -2,7 +2,7 @@ import { type FlagClient, type FlagClientOptions } from "@indigoai-us/hq-flags-c
|
|
|
2
2
|
import { type BotConfig } from "./config.js";
|
|
3
3
|
export declare const INBOX_POLL_BACKOFF_FLAG = "agents.inbox-poll-backoff-local-cli";
|
|
4
4
|
export declare const INBOX_POLL_BACKOFF_FLAG_FALLBACK = false;
|
|
5
|
-
export declare const INBOX_POLL_BACKOFF_FLAG_TIMEOUT_MS =
|
|
5
|
+
export declare const INBOX_POLL_BACKOFF_FLAG_TIMEOUT_MS = 2000;
|
|
6
6
|
export declare const INBOX_POLL_BACKOFF_FLAG_CACHE_MS: number;
|
|
7
7
|
type InboxPollBackoffFlagClient = Pick<FlagClient, "ready" | "isEnabled" | "close">;
|
|
8
8
|
export interface InboxPollBackoffFlagReaderDependencies {
|
|
@@ -5,7 +5,10 @@ import { resolveFlagRegistryEndpoint } from "../flag-registry-endpoint.js";
|
|
|
5
5
|
import { effectiveBotCompanies, effectiveBotKind } from "./config.js";
|
|
6
6
|
export const INBOX_POLL_BACKOFF_FLAG = "agents.inbox-poll-backoff-local-cli";
|
|
7
7
|
export const INBOX_POLL_BACKOFF_FLAG_FALLBACK = false;
|
|
8
|
-
|
|
8
|
+
// One read every three minutes on a fresh connection. From a home network the
|
|
9
|
+
// TCP and TLS handshakes plus the resolve call take about 250-400 ms, so a
|
|
10
|
+
// 150 ms budget timed out on every read and kept the gate off.
|
|
11
|
+
export const INBOX_POLL_BACKOFF_FLAG_TIMEOUT_MS = 2_000;
|
|
9
12
|
export const INBOX_POLL_BACKOFF_FLAG_CACHE_MS = 3 * 60_000;
|
|
10
13
|
const COMPANY_UID_RE = /^cmp_[A-Za-z0-9]{3,128}$/;
|
|
11
14
|
export function safeErrorClass(error) {
|
|
@@ -77,7 +77,7 @@ export interface DuplicateGroup {
|
|
|
77
77
|
/** `same_name`: equal canonical names, or one's canonical name is the other's unshared alias. `similar_name`: canonical names match once spaces are removed. */
|
|
78
78
|
reason: "same_name" | "similar_name";
|
|
79
79
|
paths: string[];
|
|
80
|
-
/** Highest `signal_count`, then earliest `first_seen`, then path. */
|
|
80
|
+
/** Highest `signal_count`, then earliest `first_seen`, then a file that is not a numbered copy (`<slug> 2.md`), then path. */
|
|
81
81
|
survivor: string;
|
|
82
82
|
}
|
|
83
83
|
export interface DuplicateReport {
|
|
@@ -285,6 +285,8 @@ export function mergeBackConflicts(hqRoot, company, now) {
|
|
|
285
285
|
}
|
|
286
286
|
return run;
|
|
287
287
|
}
|
|
288
|
+
/** A copy saved next to the original file, e.g. `hq-core 2.md`. */
|
|
289
|
+
const NUMBERED_COPY = / \d+\.md$/;
|
|
288
290
|
/** Groups of writable entities of one type whose names collide. Read-only. */
|
|
289
291
|
export function findDuplicateGroups(index) {
|
|
290
292
|
const parent = new Map();
|
|
@@ -349,6 +351,7 @@ export function findDuplicateGroups(index) {
|
|
|
349
351
|
const fb = Date.parse(eb.first_seen ?? "");
|
|
350
352
|
return (eb.signal_count - ea.signal_count ||
|
|
351
353
|
(Number.isFinite(fa) ? fa : Infinity) - (Number.isFinite(fb) ? fb : Infinity) ||
|
|
354
|
+
Number(NUMBERED_COPY.test(a)) - Number(NUMBERED_COPY.test(b)) ||
|
|
352
355
|
a.localeCompare(b));
|
|
353
356
|
});
|
|
354
357
|
out.push({
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* message instead of every command parsing and printing a server response.
|
|
5
5
|
*/
|
|
6
6
|
import { renderPersonalPlanFeatureNotice } from "../lib/plan-limit-nag.js";
|
|
7
|
+
import { withCliLimitEntrySurface } from "../lib/billing/plan-limit-upgrade-url.js";
|
|
7
8
|
function planGateReason(code, details) {
|
|
8
9
|
if (details.reason?.trim())
|
|
9
10
|
return details.reason.trim();
|
|
@@ -27,11 +28,12 @@ function planGateReason(code, details) {
|
|
|
27
28
|
/** One renderer for server-selected upgrade links across CLI plan refusals. */
|
|
28
29
|
export function formatUpgradeRefusal(reason, upgradeUrl, options = {}) {
|
|
29
30
|
const safeUrl = safeUpgradeUrl(upgradeUrl);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
const cliLimitUrl = safeUrl ? withCliLimitEntrySurface(safeUrl) : undefined;
|
|
32
|
+
if (options.urlOnly && cliLimitUrl)
|
|
33
|
+
return cliLimitUrl;
|
|
34
|
+
if (!cliLimitUrl)
|
|
33
35
|
return reason;
|
|
34
|
-
return `${reason}\nUpgrade: ${
|
|
36
|
+
return `${reason}\nUpgrade: ${cliLimitUrl}`;
|
|
35
37
|
}
|
|
36
38
|
function safeUpgradeUrl(value) {
|
|
37
39
|
if (typeof value !== "string" ||
|
|
@@ -94,6 +96,7 @@ export async function offerUpgradeLink(url, deps = {}) {
|
|
|
94
96
|
const safeUrl = safeUpgradeUrl(url);
|
|
95
97
|
if (!safeUrl)
|
|
96
98
|
return false;
|
|
99
|
+
const cliLimitUrl = withCliLimitEntrySurface(safeUrl);
|
|
97
100
|
if (!(deps.interactive ?? canOfferTeamUpgrade(deps.argv)))
|
|
98
101
|
return false;
|
|
99
102
|
let answer;
|
|
@@ -113,11 +116,11 @@ export async function offerUpgradeLink(url, deps = {}) {
|
|
|
113
116
|
if (!/^y(?:es)?$/i.test(answer.trim()))
|
|
114
117
|
return false;
|
|
115
118
|
if (deps.openUrl) {
|
|
116
|
-
await deps.openUrl(
|
|
119
|
+
await deps.openUrl(cliLimitUrl);
|
|
117
120
|
}
|
|
118
121
|
else {
|
|
119
122
|
const { default: open } = await import("open");
|
|
120
|
-
await open(
|
|
123
|
+
await open(cliLimitUrl);
|
|
121
124
|
}
|
|
122
125
|
return true;
|
|
123
126
|
}
|