@indigoai-us/hq-cli 5.342.15 → 5.342.17
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/dist/commands/agents.d.ts +3 -6
- package/dist/commands/agents.js +36 -17
- package/dist/commands/bot.js +4 -16
- package/dist/commands/daemon.d.ts +4 -0
- package/dist/commands/daemon.js +5 -18
- package/dist/commands/integrations-connect.d.ts +12 -0
- package/dist/commands/integrations-connect.js +5 -1
- package/dist/commands/monitor.d.ts +1 -0
- package/dist/commands/monitor.js +4 -3
- package/dist/lib/agents/terminal-wait.d.ts +4 -0
- package/dist/lib/agents/terminal-wait.js +6 -0
- package/dist/lib/flag-registry.js +3 -1
- package/dist/lib/flag-snapshot-file.d.ts +3 -0
- package/dist/lib/flag-snapshot-file.js +190 -30
- package/dist/lib/lanes/keep-alive-audit.js +0 -2
- package/dist/lib/lanes/reconcile.js +23 -1
- package/dist/lib/lanes/types.d.ts +1 -1
- package/dist/lib/log-follow.d.ts +3 -0
- package/dist/lib/log-follow.js +20 -0
- package/dist/utils/resident-package-preload.d.ts +2 -2
- package/dist/utils/resident-package-preload.js +91 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.342.17] — 2026-10-04
|
|
6
|
+
- Show agent display names in the agents list and accept them when selecting an agent.
|
|
7
|
+
- Resume due blocked keep-alive for story-less standing lead lanes, record no-open-story skips in lane history, and report the latest blocked skip in keep-alive audit.
|
|
8
|
+
|
|
9
|
+
## [5.342.16] — 2026-10-04
|
|
10
|
+
- Keep resident command leases until monitor, agent, OAuth, bot, daemon and lane-watch runtime modules and their transitive Cognito, first-sync, hq-cloud and local-file dependencies are preloaded.
|
|
11
|
+
- CI now runs the core utility parity assertions on Ubuntu, macOS, and Windows.
|
|
12
|
+
- Retry permission-denied flag snapshot cache writes and reads in a uid-owned private fallback, while preserving captures with bounded fallback tags when recovery is unsafe or fails.
|
|
13
|
+
|
|
5
14
|
## [5.342.15] — 2026-10-04
|
|
6
15
|
- Preserve bounded integration subcommand and RPC error context on asynchronous gateway failures, and retain diagnostics for malformed HTTP 200 gateway errors (HQ-CLI-C8/C9).
|
|
7
16
|
- Claude admission can prioritize lanes that are seniors of live lanes and keep configurable per-host capacity reserved for them when `lanes.lead-admission-priority` is enabled.
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* the caller's single active membership (same as `members.ts`).
|
|
29
29
|
*/
|
|
30
30
|
import { Command } from "commander";
|
|
31
|
+
import { type ResidentCommandLeaseOptions } from "../utils/resident-package-preload.js";
|
|
31
32
|
import { type TerminalSessionPayload } from "@indigoai-us/hq-cloud/outposts/node";
|
|
32
33
|
import { type AgentModelProvider, type AgentModelCatalog } from "../lib/agents/model-catalog.js";
|
|
33
34
|
import { type BillingErrorPayload } from "../utils/billing-gate.js";
|
|
@@ -126,6 +127,7 @@ export interface CompanyAgentView {
|
|
|
126
127
|
codexModel?: string;
|
|
127
128
|
codexReasoningEffort?: string;
|
|
128
129
|
codexServiceTier?: string;
|
|
130
|
+
displayName?: string;
|
|
129
131
|
profile?: {
|
|
130
132
|
displayName?: string;
|
|
131
133
|
title?: string;
|
|
@@ -367,12 +369,6 @@ export interface DmThreadMessage {
|
|
|
367
369
|
/** "out" = sent by the caller; "in" = received (the agent's reply). */
|
|
368
370
|
direction: "in" | "out";
|
|
369
371
|
}
|
|
370
|
-
/**
|
|
371
|
-
* Resolve an agent reference to its `agt_` uid. An `agt_…` value is used
|
|
372
|
-
* directly; anything else is treated as a slug or display name and resolved
|
|
373
|
-
* against the company's roster (requires `--company` or a single active
|
|
374
|
-
* membership).
|
|
375
|
-
*/
|
|
376
372
|
export declare function resolveAgentUid(token: string, ref: string, companySlug: string | undefined): Promise<string>;
|
|
377
373
|
/**
|
|
378
374
|
* Fail-closed local guard for the off-box `hq agents jobs` operator commands.
|
|
@@ -403,6 +399,7 @@ export declare function sendAgentDm(token: string, agentUid: string, message: st
|
|
|
403
399
|
export declare function readAgentThread(token: string, agentUid: string, limit?: number): Promise<DmThreadMessage[]>;
|
|
404
400
|
/** Render one thread message as a labeled line for the terminal. */
|
|
405
401
|
export declare function formatThreadMessage(m: DmThreadMessage, agentLabel: string): string;
|
|
402
|
+
export declare function waitForAgentMessageWithPackageLease<T>(waitForReply: () => Promise<T>, leaseOptions?: ResidentCommandLeaseOptions): Promise<T>;
|
|
406
403
|
export interface EnrollmentIssue {
|
|
407
404
|
code: string;
|
|
408
405
|
expiresAt: string;
|
package/dist/commands/agents.js
CHANGED
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
* (`hq agents list --company acme`); when omitted, resolution falls back to
|
|
28
28
|
* the caller's single active membership (same as `members.ts`).
|
|
29
29
|
*/
|
|
30
|
+
import { runResidentCommandWithPackageLease, } from "../utils/resident-package-preload.js";
|
|
31
|
+
import { waitForAgentTerminalSession } from "../lib/agents/terminal-wait.js";
|
|
30
32
|
import chalk from "chalk";
|
|
31
33
|
import { randomUUID } from "node:crypto";
|
|
32
34
|
import * as readline from "node:readline";
|
|
@@ -37,7 +39,7 @@ import { gateApiKeyCapabilities } from "../utils/api-key-command-gate.js";
|
|
|
37
39
|
import { queueCliLimitNoticeEngagement, queueCliLimitNoticeExposure, } from "../utils/cli-telemetry.js";
|
|
38
40
|
import { vaultApiFetch, getCompanyUid } from "../utils/vault-api.js";
|
|
39
41
|
import { consoleOrigin } from "./integrations-connect.js";
|
|
40
|
-
import { launchSessionManagerPlugin, SessionManagerPluginLaunchError, TerminalSessionTimeoutError,
|
|
42
|
+
import { launchSessionManagerPlugin, SessionManagerPluginLaunchError, TerminalSessionTimeoutError, } from "@indigoai-us/hq-cloud/outposts/node";
|
|
41
43
|
import { peekIdToken } from "../utils/id-token.js";
|
|
42
44
|
import { canOfferTeamUpgrade, formatUpgradeRefusal, isPlanGateError, offerUpgradeLink, } from "../utils/plan-gate-error.js";
|
|
43
45
|
import { AGENT_UID_PATTERN, isAgentUid } from "../lib/notify-recipient.js";
|
|
@@ -994,15 +996,26 @@ function catalogUnavailableMessage() {
|
|
|
994
996
|
* against the company's roster (requires `--company` or a single active
|
|
995
997
|
* membership).
|
|
996
998
|
*/
|
|
999
|
+
function effectiveAgentDisplayName(agent) {
|
|
1000
|
+
return agent.profile?.displayName || agent.displayName || agent.name || "";
|
|
1001
|
+
}
|
|
997
1002
|
export async function resolveAgentUid(token, ref, companySlug) {
|
|
998
1003
|
if (AGENT_UID_PATTERN.test(ref.trim()))
|
|
999
1004
|
return ref.trim();
|
|
1000
1005
|
const companyUid = await getCompanyUid(token, companySlug);
|
|
1001
1006
|
const roster = await listAgents(token, companyUid);
|
|
1002
|
-
const
|
|
1003
|
-
const
|
|
1004
|
-
|
|
1005
|
-
|
|
1007
|
+
const trimmedRef = ref.trim();
|
|
1008
|
+
const needle = trimmedRef.toLowerCase();
|
|
1009
|
+
const exactMatch = roster.find((agent) => agent.slug?.toLowerCase() === needle) ??
|
|
1010
|
+
roster.find((agent) => agent.name?.toLowerCase() === needle) ??
|
|
1011
|
+
roster.find((agent) => agent.uid === trimmedRef);
|
|
1012
|
+
if (exactMatch)
|
|
1013
|
+
return exactMatch.uid;
|
|
1014
|
+
const displayNameMatches = roster.filter((agent) => effectiveAgentDisplayName(agent).toLowerCase() === needle);
|
|
1015
|
+
if (displayNameMatches.length > 1) {
|
|
1016
|
+
throw new AgentsHttpError(409, `More than one agent matches "${ref}" in this company. Use an agent slug or agt_ UID.`);
|
|
1017
|
+
}
|
|
1018
|
+
const match = displayNameMatches[0];
|
|
1006
1019
|
if (!match) {
|
|
1007
1020
|
throw new AgentsHttpError(404, `No agent named "${ref}" in this company. Run \`hq agents list\` to see slugs/uids, ` +
|
|
1008
1021
|
`or pass the agt_ uid directly.`);
|
|
@@ -1114,6 +1127,9 @@ function shortTime(iso) {
|
|
|
1114
1127
|
const m = iso.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}:\d{2})/);
|
|
1115
1128
|
return m ? `${m[2]}-${m[3]} ${m[4]}` : iso;
|
|
1116
1129
|
}
|
|
1130
|
+
export function waitForAgentMessageWithPackageLease(waitForReply, leaseOptions = {}) {
|
|
1131
|
+
return runResidentCommandWithPackageLease("agents-message-wait", waitForReply, leaseOptions);
|
|
1132
|
+
}
|
|
1117
1133
|
function sleep(ms) {
|
|
1118
1134
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1119
1135
|
}
|
|
@@ -1307,14 +1323,17 @@ export function registerAgentsCommand(program) {
|
|
|
1307
1323
|
const deadline = Date.now() + timeoutMs;
|
|
1308
1324
|
if (!opts.json)
|
|
1309
1325
|
console.log(chalk.dim(`Sent — waiting for ${agent} to reply…`));
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1326
|
+
const reply = await waitForAgentMessageWithPackageLease(async () => {
|
|
1327
|
+
let received;
|
|
1328
|
+
while (Date.now() < deadline) {
|
|
1329
|
+
await sleep(3000);
|
|
1330
|
+
const msgs = await readAgentThread(token, agentUid, 10);
|
|
1331
|
+
received = msgs.find((m) => m.direction === "in" && m.createdAt > lastBeforeAt);
|
|
1332
|
+
if (received)
|
|
1333
|
+
break;
|
|
1334
|
+
}
|
|
1335
|
+
return received;
|
|
1336
|
+
});
|
|
1318
1337
|
if (opts.json) {
|
|
1319
1338
|
process.stdout.write(JSON.stringify({ agentUid, sent: message, reply: reply ?? null }, null, 2) + "\n");
|
|
1320
1339
|
return;
|
|
@@ -1391,7 +1410,7 @@ export function registerAgentsCommand(program) {
|
|
|
1391
1410
|
// registration after a cold start takes ~30-90s. Status lines go to
|
|
1392
1411
|
// stderr (once per state) so stdout stays the session's.
|
|
1393
1412
|
let lastState;
|
|
1394
|
-
const session = await
|
|
1413
|
+
const session = await runResidentCommandWithPackageLease("agents-terminal", () => waitForAgentTerminalSession(async () => {
|
|
1395
1414
|
const result = await openAgentTerminal(token, agentUid, body);
|
|
1396
1415
|
if (result.ok)
|
|
1397
1416
|
return { kind: "ready", session: result };
|
|
@@ -1411,7 +1430,7 @@ export function registerAgentsCommand(program) {
|
|
|
1411
1430
|
? `${agent}'s box is starting — waiting for it to come online (Ctrl-C to cancel)…`
|
|
1412
1431
|
: `${agent}'s box is up — waiting for its agent to register (Ctrl-C to cancel)…`));
|
|
1413
1432
|
},
|
|
1414
|
-
});
|
|
1433
|
+
}));
|
|
1415
1434
|
console.error(chalk.dim(forwardPort !== undefined
|
|
1416
1435
|
? `Forwarding box port ${forwardPort} to localhost:${localPort ?? forwardPort} — Ctrl-C to end.`
|
|
1417
1436
|
: `Connected to ${agent}'s box — type 'exit' or Ctrl-D to leave.`));
|
|
@@ -1689,7 +1708,7 @@ export function registerAgentsCommand(program) {
|
|
|
1689
1708
|
console.log(chalk.gray("No agents found for this company."));
|
|
1690
1709
|
return;
|
|
1691
1710
|
}
|
|
1692
|
-
const nameW = Math.max(4, ...roster.map((
|
|
1711
|
+
const nameW = Math.max(4, ...roster.map((agent) => effectiveAgentDisplayName(agent).length));
|
|
1693
1712
|
const slugW = Math.max(4, ...roster.map((a) => (a.slug ?? "").length));
|
|
1694
1713
|
const statusW = Math.max(6, ...roster.map((a) => rosterStatusLabel(a).length));
|
|
1695
1714
|
const modelW = Math.max(5, ...roster.map((a) => (a.codexModel ?? "").length));
|
|
@@ -1702,7 +1721,7 @@ export function registerAgentsCommand(program) {
|
|
|
1702
1721
|
].join(" ")));
|
|
1703
1722
|
for (const a of roster) {
|
|
1704
1723
|
console.log([
|
|
1705
|
-
(a
|
|
1724
|
+
effectiveAgentDisplayName(a).padEnd(nameW),
|
|
1706
1725
|
(a.slug ?? "").padEnd(slugW),
|
|
1707
1726
|
rosterStatusLabel(a).padEnd(statusW),
|
|
1708
1727
|
(a.codexModel ?? "").padEnd(modelW),
|
package/dist/commands/bot.js
CHANGED
|
@@ -27,7 +27,8 @@ import { hasPromotionHold, promotionHoldSummary } from "../lib/bot/promotion-hol
|
|
|
27
27
|
*/
|
|
28
28
|
import { promoteLocalBot } from "../lib/bot/promote.js";
|
|
29
29
|
import { releaseCurrentPackageUseLease, releasePackageUseLeaseAfterResidentPreload } from "../utils/package-use-lease.js";
|
|
30
|
-
import { preloadResidentPackageModules, reportResidentPackagePreloadFailure } from "../utils/resident-package-preload.js";
|
|
30
|
+
import { preloadResidentPackageModules, reportResidentPackagePreloadFailure, runResidentCommandWithPackageLease, } from "../utils/resident-package-preload.js";
|
|
31
|
+
import { followLogFile } from "../lib/log-follow.js";
|
|
31
32
|
import { CLI_UPDATE_DISABLE_ENV, createCliUpdateWatcher, handOffToUpdatedCli } from "../lib/bot/cli-update.js";
|
|
32
33
|
import { createVaultObjectDownloader } from "../lib/agent-kit/attachment-download.js";
|
|
33
34
|
import { addBotToCompanies, describeCompanyFailures, removeBotFromCompanies } from "./bot-companies.js";
|
|
@@ -1011,21 +1012,8 @@ async function runBotLogs(nameArg, opts) {
|
|
|
1011
1012
|
console.log(line);
|
|
1012
1013
|
if (!opts.follow)
|
|
1013
1014
|
return;
|
|
1014
|
-
|
|
1015
|
-
await
|
|
1016
|
-
fs.watchFile(file, { interval: 500 }, (curr) => {
|
|
1017
|
-
if (curr.size < size)
|
|
1018
|
-
size = 0;
|
|
1019
|
-
if (curr.size > size) {
|
|
1020
|
-
const fd = fs.openSync(file, "r");
|
|
1021
|
-
const buf = Buffer.alloc(curr.size - size);
|
|
1022
|
-
fs.readSync(fd, buf, 0, buf.length, size);
|
|
1023
|
-
fs.closeSync(fd);
|
|
1024
|
-
process.stdout.write(buf.toString());
|
|
1025
|
-
size = curr.size;
|
|
1026
|
-
}
|
|
1027
|
-
});
|
|
1028
|
-
});
|
|
1015
|
+
const size = fs.statSync(file).size;
|
|
1016
|
+
await runResidentCommandWithPackageLease("bot-logs-follow", () => followLogFile(file, size, (chunk) => process.stdout.write(chunk)));
|
|
1029
1017
|
}
|
|
1030
1018
|
// ── rm ───────────────────────────────────────────────────────────────────────
|
|
1031
1019
|
async function confirm(question) {
|
|
@@ -64,6 +64,10 @@ export declare function resolveDaemonHqRoot(flag: string | undefined, where: {
|
|
|
64
64
|
cwd: string;
|
|
65
65
|
home: string;
|
|
66
66
|
}): string | undefined;
|
|
67
|
+
export declare function runLogs(d: DaemonCommandDeps, unit: string | undefined, opts: {
|
|
68
|
+
follow?: boolean;
|
|
69
|
+
lines?: string;
|
|
70
|
+
}): Promise<void>;
|
|
67
71
|
export declare function registerDaemonCommand(program: Command, over?: Partial<DaemonCommandDeps>): Command;
|
|
68
72
|
export {};
|
|
69
73
|
//# sourceMappingURL=daemon.d.ts.map
|
package/dist/commands/daemon.js
CHANGED
|
@@ -36,7 +36,8 @@ import { hqdRequest, hqdSocketPath, HqdClientError } from "../lib/daemon/protoco
|
|
|
36
36
|
import { parseStdinMachineCredentials } from "../lib/daemon/machine-login.js";
|
|
37
37
|
import { resolveDefaultHqRoot } from "../utils/cognito-session.js";
|
|
38
38
|
import { releaseCurrentPackageUseLease, releasePackageUseLeaseAfterResidentPreload } from "../utils/package-use-lease.js";
|
|
39
|
-
import { preloadResidentPackageModules, reportResidentPackagePreloadFailure } from "../utils/resident-package-preload.js";
|
|
39
|
+
import { preloadResidentPackageModules, reportResidentPackagePreloadFailure, runResidentCommandWithPackageLease, } from "../utils/resident-package-preload.js";
|
|
40
|
+
import { followLogFile } from "../lib/log-follow.js";
|
|
40
41
|
/** Load every lazy daemon runtime module before releasing its package-use lease. */
|
|
41
42
|
export async function loadDaemonRunner(release = releaseCurrentPackageUseLease, load = () => import("../lib/daemon/run.js"), preload = () => preloadResidentPackageModules("daemon"), reportPreloadFailure = reportResidentPackagePreloadFailure) {
|
|
42
43
|
const { runDaemon } = await load();
|
|
@@ -401,7 +402,7 @@ function tailLines(file, lines) {
|
|
|
401
402
|
const text = fs.readFileSync(file, "utf8");
|
|
402
403
|
return text.split("\n").filter((l, i, all) => i < all.length - 1 || l !== "").slice(-lines);
|
|
403
404
|
}
|
|
404
|
-
async function runLogs(d, unit, opts) {
|
|
405
|
+
export async function runLogs(d, unit, opts) {
|
|
405
406
|
const { paths } = currentHost(d);
|
|
406
407
|
const file = unit ? paths.unitLogPath(unit) : paths.supervisorLogPath;
|
|
407
408
|
if (!fs.existsSync(file))
|
|
@@ -411,22 +412,8 @@ async function runLogs(d, unit, opts) {
|
|
|
411
412
|
d.out(line);
|
|
412
413
|
if (!opts.follow)
|
|
413
414
|
return;
|
|
414
|
-
|
|
415
|
-
await
|
|
416
|
-
setInterval(() => {
|
|
417
|
-
const size = fs.statSync(file).size;
|
|
418
|
-
if (size < offset)
|
|
419
|
-
offset = 0;
|
|
420
|
-
if (size === offset)
|
|
421
|
-
return;
|
|
422
|
-
const fd = fs.openSync(file, "r");
|
|
423
|
-
const buf = Buffer.alloc(size - offset);
|
|
424
|
-
fs.readSync(fd, buf, 0, buf.length, offset);
|
|
425
|
-
fs.closeSync(fd);
|
|
426
|
-
offset = size;
|
|
427
|
-
process.stdout.write(buf.toString("utf8"));
|
|
428
|
-
}, 500);
|
|
429
|
-
});
|
|
415
|
+
const offset = fs.statSync(file).size;
|
|
416
|
+
await runResidentCommandWithPackageLease("daemon-logs-follow", () => followLogFile(file, offset, (chunk) => process.stdout.write(chunk)));
|
|
430
417
|
}
|
|
431
418
|
function queueRequest(d, request, what) {
|
|
432
419
|
const host = currentHost(d);
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* "key" but is really OAuth-protected still connects on the first try.
|
|
18
18
|
*/
|
|
19
19
|
import { Command } from "commander";
|
|
20
|
+
import { type ResidentCommandLeaseOptions } from "../utils/resident-package-preload.js";
|
|
20
21
|
import { consoleOrigin } from "../utils/console-origin.js";
|
|
21
22
|
export { consoleOrigin };
|
|
22
23
|
import type { AdminConnection } from "./integrations-core.js";
|
|
@@ -62,6 +63,17 @@ interface ConnectTarget {
|
|
|
62
63
|
/** Human label for prompts and success lines. */
|
|
63
64
|
label: string;
|
|
64
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Run the browser sign-in and finish the install.
|
|
68
|
+
*
|
|
69
|
+
* The loopback listener is bound BEFORE `/oauth/start` because its port is
|
|
70
|
+
* part of the redirect URI hq-pro registers with the remote authorization
|
|
71
|
+
* server. If hq-pro refuses that URI (its loopback flag is off, or it predates
|
|
72
|
+
* the feature), the flow degrades to the console handoff rather than failing:
|
|
73
|
+
* `/oauth/start` is retried with no `redirectUri`, and the caller is told to
|
|
74
|
+
* finish in the browser where the console callback completes it.
|
|
75
|
+
*/
|
|
76
|
+
export declare function waitForIntegrationOAuthWithPackageLease<T>(waitForCode: () => Promise<T>, leaseOptions?: ResidentCommandLeaseOptions): Promise<T>;
|
|
65
77
|
/**
|
|
66
78
|
* The console's Integrations page for a company, the console root when the
|
|
67
79
|
* caller never named one (a single-membership session resolves the company
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import chalk from "chalk";
|
|
20
20
|
import open from "open";
|
|
21
21
|
import { ensureCognitoIdToken } from "../utils/cognito-session.js";
|
|
22
|
+
import { runResidentCommandWithPackageLease, } from "../utils/resident-package-preload.js";
|
|
22
23
|
import { consoleOrigin } from "../utils/console-origin.js";
|
|
23
24
|
export { consoleOrigin };
|
|
24
25
|
import { getCompanyUid } from "../utils/vault-api.js";
|
|
@@ -458,6 +459,9 @@ async function assertTargetResolvable(token, companyUid, target) {
|
|
|
458
459
|
* `/oauth/start` is retried with no `redirectUri`, and the caller is told to
|
|
459
460
|
* finish in the browser where the console callback completes it.
|
|
460
461
|
*/
|
|
462
|
+
export function waitForIntegrationOAuthWithPackageLease(waitForCode, leaseOptions = {}) {
|
|
463
|
+
return runResidentCommandWithPackageLease("integrations-oauth-wait", waitForCode, leaseOptions);
|
|
464
|
+
}
|
|
461
465
|
async function connectViaOAuth(token, companyUid, target, opts) {
|
|
462
466
|
const timeoutMs = opts.timeout ? Number(opts.timeout) * 1000 : undefined;
|
|
463
467
|
if (timeoutMs !== undefined && (!Number.isFinite(timeoutMs) || timeoutMs <= 0)) {
|
|
@@ -529,7 +533,7 @@ async function connectViaOAuth(token, companyUid, target, opts) {
|
|
|
529
533
|
}
|
|
530
534
|
let code;
|
|
531
535
|
try {
|
|
532
|
-
code = await pendingCode;
|
|
536
|
+
code = await waitForIntegrationOAuthWithPackageLease(() => pendingCode);
|
|
533
537
|
}
|
|
534
538
|
catch (err) {
|
|
535
539
|
if (err instanceof IntegrationsCliError &&
|
|
@@ -40,6 +40,7 @@ export interface RunSupervisorDeps {
|
|
|
40
40
|
reportPreloadFailure?: (error: unknown) => void;
|
|
41
41
|
createLaneMessageApi?: typeof createMonitorLaneMessageApi;
|
|
42
42
|
}
|
|
43
|
+
/** Preload every monitor runtime; only lane targets need the in-process message API. */
|
|
43
44
|
export declare function preloadMonitorLaneMessageApi(hqRoot: string, meta: MonitorMetadata, deps?: Pick<RunSupervisorDeps, "preloadResidentModules" | "releasePackageUseLease" | "reportPreloadFailure" | "createLaneMessageApi">): Promise<MonitorLaneMessageApi | undefined>;
|
|
44
45
|
export declare function runSupervisor(id: string, deps?: RunSupervisorDeps): Promise<void>;
|
|
45
46
|
interface MonitorHookResult {
|
package/dist/commands/monitor.js
CHANGED
|
@@ -575,13 +575,14 @@ function activeTargetMeta(hqRoot, targetKey) {
|
|
|
575
575
|
return meta?.target_key === targetKey && state?.status === "running" && state.pid !== null && readMonitorPidLiveness(state.pid, state.starttime) ? [meta] : [];
|
|
576
576
|
});
|
|
577
577
|
}
|
|
578
|
+
/** Preload every monitor runtime; only lane targets need the in-process message API. */
|
|
578
579
|
export async function preloadMonitorLaneMessageApi(hqRoot, meta, deps = {}) {
|
|
579
|
-
if (meta.target.kind !== "lane")
|
|
580
|
-
return undefined;
|
|
581
580
|
let laneMessageApi;
|
|
582
581
|
await releasePackageUseLeaseAfterResidentPreload(async () => {
|
|
583
582
|
await (deps.preloadResidentModules ?? (() => preloadResidentPackageModules("monitor-run")))();
|
|
584
|
-
|
|
583
|
+
if (meta.target.kind === "lane") {
|
|
584
|
+
laneMessageApi = (deps.createLaneMessageApi ?? createMonitorLaneMessageApi)(hqRoot, meta.cwd, process.env);
|
|
585
|
+
}
|
|
585
586
|
}, deps.reportPreloadFailure ?? reportResidentPackagePreloadFailure, deps.releasePackageUseLease ?? releaseCurrentPackageUseLease);
|
|
586
587
|
return laneMessageApi;
|
|
587
588
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { TerminalSessionAttempt, TerminalSessionPayload, WaitForTerminalSessionOptions } from "@indigoai-us/hq-cloud/outposts/node";
|
|
2
|
+
/** Keep the terminal poll loop behind a small, auditable resident preload root. */
|
|
3
|
+
export declare function waitForAgentTerminalSession<T extends TerminalSessionPayload>(attempt: () => Promise<TerminalSessionAttempt<T>>, options?: WaitForTerminalSessionOptions): Promise<T>;
|
|
4
|
+
//# sourceMappingURL=terminal-wait.d.ts.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { waitForTerminalSession } from "@indigoai-us/hq-cloud/outposts/node";
|
|
2
|
+
/** Keep the terminal poll loop behind a small, auditable resident preload root. */
|
|
3
|
+
export function waitForAgentTerminalSession(attempt, options) {
|
|
4
|
+
return waitForTerminalSession(attempt, options);
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=terminal-wait.js.map
|
|
@@ -11,7 +11,7 @@ import { resolveFlagRegistryEndpoint, } from "./flag-registry-endpoint.js";
|
|
|
11
11
|
import { FLAG_REGISTRY_WORKER_SOURCE } from "./flag-registry-worker-source.generated.js";
|
|
12
12
|
import { resolveProcessFlagCompanyContext } from "./process-flag-company-context.js";
|
|
13
13
|
import { registerFlagRegistryRefreshWaiter } from "./flag-registry-refresh-wait.js";
|
|
14
|
-
import { writeFlagSnapshotFile } from "./flag-snapshot-file.js";
|
|
14
|
+
import { flagSnapshotWriteFailureTags, writeFlagSnapshotFile } from "./flag-snapshot-file.js";
|
|
15
15
|
function captureFlagRegistryException(error, tags, sentry) {
|
|
16
16
|
if (!sentry)
|
|
17
17
|
return;
|
|
@@ -63,6 +63,7 @@ export async function waitForFlagRegistryRefresh(timeoutMs = 500, companyUid) {
|
|
|
63
63
|
captureFlagRegistryException(error instanceof Error ? error : new Error(String(error)), {
|
|
64
64
|
component: "flag-snapshot-cache-write",
|
|
65
65
|
company_uid_present: "true",
|
|
66
|
+
...flagSnapshotWriteFailureTags(error),
|
|
66
67
|
}, refreshSentry);
|
|
67
68
|
}
|
|
68
69
|
refreshPendingSnapshot = null;
|
|
@@ -215,6 +216,7 @@ export function kickFlagRegistryReadiness(dependencies = {}) {
|
|
|
215
216
|
captureFlagRegistryException(error instanceof Error ? error : new Error(String(error)), {
|
|
216
217
|
component: "flag-snapshot-cache-write",
|
|
217
218
|
company_uid_present: "true",
|
|
219
|
+
...flagSnapshotWriteFailureTags(error),
|
|
218
220
|
}, sentry);
|
|
219
221
|
}
|
|
220
222
|
refreshPendingSnapshot = null;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/** Private, synchronous, offline snapshot used by the native CLI fast route. */
|
|
2
2
|
export declare const FLAG_SNAPSHOT_MAX_AGE_MS: number;
|
|
3
|
+
export type FlagSnapshotWriteStep = "mkdir" | "chmod_dir" | "open_tmp" | "write_tmp" | "rename" | "chmod_file";
|
|
4
|
+
export declare function flagSnapshotWriteFailureTags(error: unknown): Record<string, string>;
|
|
3
5
|
export interface PersistedFlagSnapshot {
|
|
4
6
|
company_uid: string;
|
|
5
7
|
fetched_at: string;
|
|
6
8
|
flags: Record<string, boolean>;
|
|
7
9
|
}
|
|
10
|
+
export declare function _resetFlagSnapshotCacheForTests(): void;
|
|
8
11
|
export declare function flagSnapshotPath(companyUid: string, env?: NodeJS.ProcessEnv): string | undefined;
|
|
9
12
|
/** Write just the validated flag decisions and company scope, never auth data. */
|
|
10
13
|
export declare function writeFlagSnapshotFile(companyUid: string, flags: Readonly<Record<string, unknown>>, options?: {
|
|
@@ -4,6 +4,104 @@ import * as os from "node:os";
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
export const FLAG_SNAPSHOT_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
6
6
|
const COMPANY_UID_RE = /^cmp_[A-Za-z0-9]{3,128}$/;
|
|
7
|
+
function markWriteFailure(error, context) {
|
|
8
|
+
if (!error || (typeof error !== "object" && typeof error !== "function"))
|
|
9
|
+
return;
|
|
10
|
+
const failure = error;
|
|
11
|
+
let dirOwnerMatchesUid = "unknown";
|
|
12
|
+
if (typeof process.getuid === "function") {
|
|
13
|
+
try {
|
|
14
|
+
dirOwnerMatchesUid = fs.statSync(context.directory).uid === process.getuid()
|
|
15
|
+
? "true"
|
|
16
|
+
: "false";
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// A missing or inaccessible directory has no safely knowable owner.
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
Object.defineProperties(failure, {
|
|
24
|
+
flagSnapshotStep: { value: context.step, configurable: true },
|
|
25
|
+
cacheRootKind: { value: context.cacheRootKind, configurable: true },
|
|
26
|
+
dirOwnerMatchesUid: { value: dirOwnerMatchesUid, configurable: true },
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Preserve the original filesystem error if it cannot carry diagnostics.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function atWriteStep(context, operation) {
|
|
34
|
+
try {
|
|
35
|
+
return operation();
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
markWriteFailure(error, context);
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function flagSnapshotWriteFailureTags(error) {
|
|
43
|
+
const failure = error;
|
|
44
|
+
const code = failure && typeof failure.code === "string" ? failure.code : "";
|
|
45
|
+
const allowedCodes = new Set(["EACCES", "EPERM", "EROFS", "ENOSPC"]);
|
|
46
|
+
const step = failure?.flagSnapshotStep;
|
|
47
|
+
const rootKind = failure?.cacheRootKind;
|
|
48
|
+
const owner = failure?.dirOwnerMatchesUid;
|
|
49
|
+
const tags = {
|
|
50
|
+
flag_snapshot_step: step && ["mkdir", "chmod_dir", "open_tmp", "write_tmp", "rename", "chmod_file"].includes(step)
|
|
51
|
+
? step
|
|
52
|
+
: "other",
|
|
53
|
+
errno_code: allowedCodes.has(code) ? code : "other",
|
|
54
|
+
cache_root_kind: rootKind === "xdg" || rootKind === "home" ? rootKind : "unknown",
|
|
55
|
+
dir_owner_matches_uid: owner === "true" || owner === "false" ? owner : "unknown",
|
|
56
|
+
};
|
|
57
|
+
if (fallbackCacheUsed)
|
|
58
|
+
tags.cache_root = "fallback";
|
|
59
|
+
if (failure?.fallbackFailed)
|
|
60
|
+
tags.fallback = "failed";
|
|
61
|
+
return tags;
|
|
62
|
+
}
|
|
63
|
+
let fallbackCacheUsed = false;
|
|
64
|
+
export function _resetFlagSnapshotCacheForTests() {
|
|
65
|
+
fallbackCacheUsed = false;
|
|
66
|
+
}
|
|
67
|
+
function markFallbackFailed(error) {
|
|
68
|
+
if (!error || (typeof error !== "object" && typeof error !== "function"))
|
|
69
|
+
return;
|
|
70
|
+
try {
|
|
71
|
+
Object.defineProperty(error, "fallbackFailed", { value: true, configurable: true });
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Keep the original filesystem error intact if it cannot carry diagnostics.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function isPermissionFailure(error) {
|
|
78
|
+
const code = error?.code;
|
|
79
|
+
return code === "EACCES" || code === "EPERM";
|
|
80
|
+
}
|
|
81
|
+
function fallbackSnapshotPath(companyUid, create) {
|
|
82
|
+
if (typeof process.getuid !== "function")
|
|
83
|
+
return undefined;
|
|
84
|
+
const uid = process.getuid();
|
|
85
|
+
const root = path.join(os.tmpdir(), `hq-cli-flag-snapshots-${uid}`);
|
|
86
|
+
const directories = [root, path.join(root, "hq"), path.join(root, "hq", "flags")];
|
|
87
|
+
for (const directory of directories) {
|
|
88
|
+
try {
|
|
89
|
+
if (create)
|
|
90
|
+
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
91
|
+
const stat = fs.lstatSync(directory);
|
|
92
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || stat.uid !== uid)
|
|
93
|
+
return undefined;
|
|
94
|
+
if (create)
|
|
95
|
+
fs.chmodSync(directory, 0o700);
|
|
96
|
+
else if ((stat.mode & 0o077) !== 0)
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return path.join(directories[2], `snapshot-${companyUid}.json`);
|
|
104
|
+
}
|
|
7
105
|
export function flagSnapshotPath(companyUid, env = process.env) {
|
|
8
106
|
if (!COMPANY_UID_RE.test(companyUid))
|
|
9
107
|
return undefined;
|
|
@@ -12,7 +110,8 @@ export function flagSnapshotPath(companyUid, env = process.env) {
|
|
|
12
110
|
}
|
|
13
111
|
/** Write just the validated flag decisions and company scope, never auth data. */
|
|
14
112
|
export function writeFlagSnapshotFile(companyUid, flags, options = {}) {
|
|
15
|
-
const
|
|
113
|
+
const env = options.env ?? process.env;
|
|
114
|
+
const file = flagSnapshotPath(companyUid, env);
|
|
16
115
|
if (!file)
|
|
17
116
|
throw new Error("Cannot persist a flag snapshot without a valid company uid");
|
|
18
117
|
const normalizedFlags = Object.fromEntries(Object.entries(flags).filter((entry) => Boolean(entry[0].trim()) && typeof entry[1] === "boolean"));
|
|
@@ -24,39 +123,78 @@ export function writeFlagSnapshotFile(companyUid, flags, options = {}) {
|
|
|
24
123
|
if (!Number.isFinite(Date.parse(snapshot.fetched_at))) {
|
|
25
124
|
throw new Error("Cannot persist a flag snapshot with an invalid fetched_at timestamp");
|
|
26
125
|
}
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
fs.
|
|
36
|
-
|
|
37
|
-
fd
|
|
38
|
-
fs.renameSync(temporary, file);
|
|
39
|
-
fs.chmodSync(file, 0o600);
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
if (fd !== undefined)
|
|
43
|
-
fs.closeSync(fd);
|
|
126
|
+
const cacheRootKind = env.XDG_CACHE_HOME?.trim() ? "xdg" : "home";
|
|
127
|
+
const writeAt = (destination) => {
|
|
128
|
+
const directory = path.dirname(destination);
|
|
129
|
+
const context = (step) => ({
|
|
130
|
+
step,
|
|
131
|
+
cacheRootKind,
|
|
132
|
+
directory,
|
|
133
|
+
});
|
|
134
|
+
atWriteStep(context("mkdir"), () => fs.mkdirSync(directory, { recursive: true, mode: 0o700 }));
|
|
135
|
+
const temporary = `${destination}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
136
|
+
let fd;
|
|
44
137
|
try {
|
|
45
|
-
fs.
|
|
138
|
+
fd = atWriteStep(context("open_tmp"), () => fs.openSync(temporary, "wx", 0o600));
|
|
139
|
+
atWriteStep(context("write_tmp"), () => {
|
|
140
|
+
fs.writeFileSync(fd, `${JSON.stringify(snapshot)}\n`, "utf8");
|
|
141
|
+
fs.fsyncSync(fd);
|
|
142
|
+
fs.closeSync(fd);
|
|
143
|
+
fd = undefined;
|
|
144
|
+
});
|
|
145
|
+
atWriteStep(context("rename"), () => fs.renameSync(temporary, destination));
|
|
46
146
|
}
|
|
47
|
-
catch (
|
|
48
|
-
if (
|
|
49
|
-
|
|
147
|
+
catch (error) {
|
|
148
|
+
if (fd !== undefined)
|
|
149
|
+
fs.closeSync(fd);
|
|
150
|
+
try {
|
|
151
|
+
fs.unlinkSync(temporary);
|
|
152
|
+
}
|
|
153
|
+
catch (cleanupError) {
|
|
154
|
+
if (cleanupError.code !== "ENOENT")
|
|
155
|
+
throw cleanupError;
|
|
156
|
+
}
|
|
157
|
+
throw error;
|
|
50
158
|
}
|
|
51
|
-
|
|
159
|
+
return destination;
|
|
160
|
+
};
|
|
161
|
+
const writeFallback = (originalError) => {
|
|
162
|
+
const fallbackFile = fallbackSnapshotPath(companyUid, true);
|
|
163
|
+
if (!fallbackFile) {
|
|
164
|
+
if (originalError) {
|
|
165
|
+
markFallbackFailed(originalError);
|
|
166
|
+
throw originalError;
|
|
167
|
+
}
|
|
168
|
+
const error = Object.assign(new Error("Flag snapshot fallback directory is unavailable"), { code: "EACCES" });
|
|
169
|
+
markFallbackFailed(error);
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const written = writeAt(fallbackFile);
|
|
174
|
+
fallbackCacheUsed = true;
|
|
175
|
+
return written;
|
|
176
|
+
}
|
|
177
|
+
catch (fallbackError) {
|
|
178
|
+
if (originalError) {
|
|
179
|
+
markFallbackFailed(originalError);
|
|
180
|
+
throw originalError;
|
|
181
|
+
}
|
|
182
|
+
markFallbackFailed(fallbackError);
|
|
183
|
+
throw fallbackError;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
if (fallbackCacheUsed)
|
|
187
|
+
return writeFallback();
|
|
188
|
+
try {
|
|
189
|
+
return writeAt(file);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
if (!isPermissionFailure(error))
|
|
193
|
+
throw error;
|
|
194
|
+
return writeFallback(error);
|
|
52
195
|
}
|
|
53
|
-
return file;
|
|
54
196
|
}
|
|
55
|
-
|
|
56
|
-
export function readFlagSnapshotFile(companyUid, options = {}) {
|
|
57
|
-
const file = flagSnapshotPath(companyUid, options.env);
|
|
58
|
-
if (!file)
|
|
59
|
-
return null;
|
|
197
|
+
function parseSnapshotFile(companyUid, file, options) {
|
|
60
198
|
let parsed;
|
|
61
199
|
try {
|
|
62
200
|
parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
@@ -71,7 +209,7 @@ export function readFlagSnapshotFile(companyUid, options = {}) {
|
|
|
71
209
|
value.company_uid !== companyUid ||
|
|
72
210
|
typeof value.fetched_at !== "string" ||
|
|
73
211
|
!value.flags || typeof value.flags !== "object" || Array.isArray(value.flags))
|
|
74
|
-
return null;
|
|
212
|
+
return null;
|
|
75
213
|
const fetchedAt = Date.parse(value.fetched_at);
|
|
76
214
|
const age = (options.now ?? Date.now()) - fetchedAt;
|
|
77
215
|
if (!Number.isFinite(fetchedAt) || age < 0 || age > (options.maxAgeMs ?? FLAG_SNAPSHOT_MAX_AGE_MS)) {
|
|
@@ -88,4 +226,26 @@ export function readFlagSnapshotFile(companyUid, options = {}) {
|
|
|
88
226
|
flags: flags,
|
|
89
227
|
};
|
|
90
228
|
}
|
|
229
|
+
/** Return null for every miss, malformed file, company mismatch, or stale file. */
|
|
230
|
+
export function readFlagSnapshotFile(companyUid, options = {}) {
|
|
231
|
+
const file = flagSnapshotPath(companyUid, options.env);
|
|
232
|
+
if (!file)
|
|
233
|
+
return null;
|
|
234
|
+
if (fallbackCacheUsed) {
|
|
235
|
+
const fallback = fallbackSnapshotPath(companyUid, false);
|
|
236
|
+
const snapshot = fallback ? parseSnapshotFile(companyUid, fallback, options) : null;
|
|
237
|
+
if (snapshot)
|
|
238
|
+
return snapshot;
|
|
239
|
+
}
|
|
240
|
+
const primary = parseSnapshotFile(companyUid, file, options);
|
|
241
|
+
if (primary)
|
|
242
|
+
return primary;
|
|
243
|
+
const fallback = fallbackSnapshotPath(companyUid, false);
|
|
244
|
+
if (!fallback)
|
|
245
|
+
return null;
|
|
246
|
+
const snapshot = parseSnapshotFile(companyUid, fallback, options);
|
|
247
|
+
if (snapshot)
|
|
248
|
+
fallbackCacheUsed = true;
|
|
249
|
+
return snapshot;
|
|
250
|
+
}
|
|
91
251
|
//# sourceMappingURL=flag-snapshot-file.js.map
|
|
@@ -355,8 +355,6 @@ function blockedKeepAliveOutcome(lane, stop, nextEnvelopeMs) {
|
|
|
355
355
|
: Math.abs(terminal - stop.mtimeMs) <= 10 * 60_000;
|
|
356
356
|
if (!sameStop)
|
|
357
357
|
return [];
|
|
358
|
-
if (entry.event === "keep_alive_blocked_skipped" && entry.reason !== "flag_off")
|
|
359
|
-
return [];
|
|
360
358
|
return [{
|
|
361
359
|
status: entry.event === "keep_alive_blocked_resumed" ? "woken" : "skipped",
|
|
362
360
|
...(entry.reason === undefined ? {} : { reason: entry.reason }),
|
|
@@ -2328,8 +2328,9 @@ async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, no
|
|
|
2328
2328
|
recordKeepAliveSkip(hqRoot, lane, "missing_interruption_time", summary, deps, "blocked terminal time is invalid");
|
|
2329
2329
|
return;
|
|
2330
2330
|
}
|
|
2331
|
-
if (activeLaneStories(lane).length === 0) {
|
|
2331
|
+
if (laneStories(lane).length > 0 && activeLaneStories(lane).length === 0) {
|
|
2332
2332
|
summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_no_open_stories" });
|
|
2333
|
+
recordBlockedKeepAliveSkipEvent(hqRoot, lane, "no_open_stories", summary, deps);
|
|
2333
2334
|
return;
|
|
2334
2335
|
}
|
|
2335
2336
|
let pendingQuestion = questions.some((question) => question.lane_id === lane.lane_id && question.status === "pending");
|
|
@@ -2496,6 +2497,27 @@ function recordBlockedKeepAliveFlagOff(hqRoot, lane, summary, deps) {
|
|
|
2496
2497
|
markDirty(summary, { kind: "lane", id: lane.lane_id, why: `blocked keep-alive skip record failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
2497
2498
|
}
|
|
2498
2499
|
}
|
|
2500
|
+
function recordBlockedKeepAliveSkipEvent(hqRoot, lane, reason, summary, deps) {
|
|
2501
|
+
const terminalAt = lane.last_envelope?.at;
|
|
2502
|
+
if (deps.dryRun || lane.last_envelope?.decision !== "blocked" || terminalAt === undefined)
|
|
2503
|
+
return;
|
|
2504
|
+
if (lane.status_gate_history?.some((entry) => entry.event === "keep_alive_blocked_skipped" && entry.terminal_at === terminalAt && entry.reason === reason))
|
|
2505
|
+
return;
|
|
2506
|
+
try {
|
|
2507
|
+
casLaneIf(hqRoot, lane.lane_id, (current) => current.version === lane.version && current.state === "blocked" && current.last_envelope?.at === terminalAt, (current) => ({
|
|
2508
|
+
...current,
|
|
2509
|
+
status_gate_history: appendCappedHistory(current.status_gate_history ?? [], {
|
|
2510
|
+
event: "keep_alive_blocked_skipped",
|
|
2511
|
+
at: deps.now().toISOString(),
|
|
2512
|
+
terminal_at: terminalAt,
|
|
2513
|
+
reason,
|
|
2514
|
+
}),
|
|
2515
|
+
}), false);
|
|
2516
|
+
}
|
|
2517
|
+
catch (err) {
|
|
2518
|
+
markDirty(summary, { kind: "lane", id: lane.lane_id, why: `blocked keep-alive skip record failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2499
2521
|
function appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, at, reason, deps) {
|
|
2500
2522
|
if (deps.dryRun || lane.status_gate_history?.some((entry) => entry.event === "keep_alive_blocked_grace" && entry.terminal_at === terminalAt && entry.resume_skipped_reason === reason))
|
|
2501
2523
|
return;
|
|
@@ -449,7 +449,7 @@ export interface KeepAliveBlockedHistoryEntry {
|
|
|
449
449
|
at: string;
|
|
450
450
|
terminal_at: string;
|
|
451
451
|
resume_skipped_reason?: "open_question" | "blocked_grace";
|
|
452
|
-
reason?: "flag_off" | "backoff" | "next_check_at_due";
|
|
452
|
+
reason?: "flag_off" | "backoff" | "next_check_at_due" | "no_open_stories";
|
|
453
453
|
resume_queued?: boolean;
|
|
454
454
|
}
|
|
455
455
|
export interface KeepAliveResumeHistoryEntry {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
/** Follow append-only log bytes without importing package code after startup. */
|
|
3
|
+
export async function followLogFile(file, initialOffset, write) {
|
|
4
|
+
let offset = initialOffset;
|
|
5
|
+
await new Promise(() => {
|
|
6
|
+
fs.watchFile(file, { interval: 500 }, (curr) => {
|
|
7
|
+
if (curr.size < offset)
|
|
8
|
+
offset = 0;
|
|
9
|
+
if (curr.size === offset)
|
|
10
|
+
return;
|
|
11
|
+
const fd = fs.openSync(file, "r");
|
|
12
|
+
const buf = Buffer.alloc(curr.size - offset);
|
|
13
|
+
fs.readSync(fd, buf, 0, buf.length, offset);
|
|
14
|
+
fs.closeSync(fd);
|
|
15
|
+
offset = curr.size;
|
|
16
|
+
write(buf.toString("utf8"));
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=log-follow.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type ResidentPackagePath = "agent-kit" | "agent-mcp" | "auth-login" | "bot" | "childWrapper" | "coreJobRun" | "login" | "mcp" | "daemon" | "mesh-daemon" | "lanes-watch" | "monitor-wait" | "monitor-run";
|
|
2
|
-
/**
|
|
1
|
+
export type ResidentPackagePath = "agent-kit" | "agent-mcp" | "auth-login" | "agents-terminal" | "bot" | "bot-logs-follow" | "childWrapper" | "coreJobRun" | "login" | "mcp" | "daemon" | "daemon-logs-follow" | "mesh-daemon" | "lanes-watch" | "monitor-wait" | "monitor-run" | "agents-message-wait" | "integrations-oauth-wait";
|
|
2
|
+
/** Runtime modules that resident commands may need after a wait starts. */
|
|
3
3
|
export declare const RESIDENT_PACKAGE_MODULES: Record<ResidentPackagePath, readonly string[]>;
|
|
4
4
|
export declare class ResidentPackagePreloadError extends Error {
|
|
5
5
|
readonly moduleSpecifier: string;
|
|
@@ -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"],
|
|
5
|
-
"agent-mcp": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "../commands/access.js", "open"],
|
|
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"],
|
|
6
6
|
login: [
|
|
7
7
|
"../lib/mesh/api.js",
|
|
8
8
|
"../lib/work-context/config.js",
|
|
@@ -12,6 +12,9 @@ export const RESIDENT_PACKAGE_MODULES = {
|
|
|
12
12
|
"../utils/plan-gate-error.js",
|
|
13
13
|
"../sentry.js",
|
|
14
14
|
"open",
|
|
15
|
+
"../utils/cognito-session.js",
|
|
16
|
+
"../lib/first-sync-next-step.js",
|
|
17
|
+
"@indigoai-us/hq-cloud",
|
|
15
18
|
],
|
|
16
19
|
"auth-login": [
|
|
17
20
|
"../lib/auth-login-provider-state.js",
|
|
@@ -20,7 +23,10 @@ export const RESIDENT_PACKAGE_MODULES = {
|
|
|
20
23
|
"../utils/plan-gate-error.js",
|
|
21
24
|
"../sentry.js",
|
|
22
25
|
"open",
|
|
26
|
+
"../utils/cognito-session.js",
|
|
27
|
+
"@indigoai-us/hq-cloud",
|
|
23
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"],
|
|
24
30
|
bot: [
|
|
25
31
|
"../lib/flag-registry.js",
|
|
26
32
|
"../commands/access.js",
|
|
@@ -28,7 +34,12 @@ export const RESIDENT_PACKAGE_MODULES = {
|
|
|
28
34
|
"../lib/plan-limit-nag.js",
|
|
29
35
|
"../utils/plan-gate-error.js",
|
|
30
36
|
"open",
|
|
37
|
+
"../utils/cognito-session.js",
|
|
38
|
+
"../lib/first-sync-next-step.js",
|
|
39
|
+
"../utils/local-files-overview.js",
|
|
40
|
+
"@indigoai-us/hq-cloud",
|
|
31
41
|
],
|
|
42
|
+
"bot-logs-follow": [],
|
|
32
43
|
mcp: ["../lib/mcp/tools.js", "../lib/mcp/prompts.js"],
|
|
33
44
|
childWrapper: ["../main.js"],
|
|
34
45
|
coreJobRun: ["../lib/flag-registry.js"],
|
|
@@ -42,11 +53,16 @@ export const RESIDENT_PACKAGE_MODULES = {
|
|
|
42
53
|
"@modelcontextprotocol/sdk/client/streamableHttp.js",
|
|
43
54
|
"../utils/staged-npm-install.js",
|
|
44
55
|
"open",
|
|
56
|
+
"../lib/first-sync-next-step.js",
|
|
57
|
+
"@indigoai-us/hq-cloud",
|
|
45
58
|
],
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"monitor-
|
|
59
|
+
"daemon-logs-follow": [],
|
|
60
|
+
"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"],
|
|
61
|
+
"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"],
|
|
62
|
+
"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"],
|
|
63
|
+
"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"],
|
|
64
|
+
"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"],
|
|
65
|
+
"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"],
|
|
50
66
|
};
|
|
51
67
|
const LOADERS = {
|
|
52
68
|
"agent-kit": [
|
|
@@ -57,6 +73,9 @@ const LOADERS = {
|
|
|
57
73
|
() => import("../lib/plan-limit-nag.js"),
|
|
58
74
|
() => import("../utils/plan-gate-error.js"),
|
|
59
75
|
() => import("open"),
|
|
76
|
+
() => import("../utils/cognito-session.js"),
|
|
77
|
+
() => import("../lib/first-sync-next-step.js"),
|
|
78
|
+
() => import("@indigoai-us/hq-cloud"),
|
|
60
79
|
],
|
|
61
80
|
"agent-mcp": [
|
|
62
81
|
async () => {
|
|
@@ -67,6 +86,10 @@ const LOADERS = {
|
|
|
67
86
|
() => import("../utils/plan-gate-error.js"),
|
|
68
87
|
() => import("../commands/access.js"),
|
|
69
88
|
() => import("open"),
|
|
89
|
+
() => import("../utils/cognito-session.js"),
|
|
90
|
+
() => import("../lib/first-sync-next-step.js"),
|
|
91
|
+
() => import("../utils/local-files-overview.js"),
|
|
92
|
+
() => import("@indigoai-us/hq-cloud"),
|
|
70
93
|
],
|
|
71
94
|
login: [
|
|
72
95
|
() => import("../lib/mesh/api.js"),
|
|
@@ -80,6 +103,9 @@ const LOADERS = {
|
|
|
80
103
|
preloadSentrySdk();
|
|
81
104
|
},
|
|
82
105
|
() => import("open"),
|
|
106
|
+
() => import("../utils/cognito-session.js"),
|
|
107
|
+
() => import("../lib/first-sync-next-step.js"),
|
|
108
|
+
() => import("@indigoai-us/hq-cloud"),
|
|
83
109
|
],
|
|
84
110
|
"auth-login": [
|
|
85
111
|
() => import("../lib/auth-login-provider-state.js"),
|
|
@@ -91,6 +117,20 @@ const LOADERS = {
|
|
|
91
117
|
preloadSentrySdk();
|
|
92
118
|
},
|
|
93
119
|
() => import("open"),
|
|
120
|
+
() => import("../utils/cognito-session.js"),
|
|
121
|
+
() => import("@indigoai-us/hq-cloud"),
|
|
122
|
+
],
|
|
123
|
+
"agents-terminal": [
|
|
124
|
+
async () => {
|
|
125
|
+
const { preloadSentrySdk } = await import("../sentry.js");
|
|
126
|
+
preloadSentrySdk();
|
|
127
|
+
},
|
|
128
|
+
() => import("../lib/plan-limit-nag.js"),
|
|
129
|
+
() => import("../utils/plan-gate-error.js"),
|
|
130
|
+
() => import("open"),
|
|
131
|
+
() => import("../utils/cognito-session.js"),
|
|
132
|
+
() => import("../lib/first-sync-next-step.js"),
|
|
133
|
+
() => import("@indigoai-us/hq-cloud"),
|
|
94
134
|
],
|
|
95
135
|
bot: [
|
|
96
136
|
() => import("../lib/flag-registry.js"),
|
|
@@ -102,7 +142,12 @@ const LOADERS = {
|
|
|
102
142
|
() => import("../lib/plan-limit-nag.js"),
|
|
103
143
|
() => import("../utils/plan-gate-error.js"),
|
|
104
144
|
() => import("open"),
|
|
145
|
+
() => import("../utils/cognito-session.js"),
|
|
146
|
+
() => import("../lib/first-sync-next-step.js"),
|
|
147
|
+
() => import("../utils/local-files-overview.js"),
|
|
148
|
+
() => import("@indigoai-us/hq-cloud"),
|
|
105
149
|
],
|
|
150
|
+
"bot-logs-follow": [],
|
|
106
151
|
mcp: [
|
|
107
152
|
() => import("../lib/mcp/tools.js"),
|
|
108
153
|
() => import("../lib/mcp/prompts.js"),
|
|
@@ -126,7 +171,10 @@ const LOADERS = {
|
|
|
126
171
|
() => import("@modelcontextprotocol/sdk/client/streamableHttp.js"),
|
|
127
172
|
() => import("../utils/staged-npm-install.js"),
|
|
128
173
|
() => import("open"),
|
|
174
|
+
() => import("../lib/first-sync-next-step.js"),
|
|
175
|
+
() => import("@indigoai-us/hq-cloud"),
|
|
129
176
|
],
|
|
177
|
+
"daemon-logs-follow": [],
|
|
130
178
|
"mesh-daemon": [
|
|
131
179
|
async () => {
|
|
132
180
|
const { preloadSentrySdk } = await import("../sentry.js");
|
|
@@ -135,6 +183,9 @@ const LOADERS = {
|
|
|
135
183
|
() => import("../lib/plan-limit-nag.js"),
|
|
136
184
|
() => import("../utils/plan-gate-error.js"),
|
|
137
185
|
() => import("open"),
|
|
186
|
+
() => import("../utils/cognito-session.js"),
|
|
187
|
+
() => import("../lib/first-sync-next-step.js"),
|
|
188
|
+
() => import("@indigoai-us/hq-cloud"),
|
|
138
189
|
],
|
|
139
190
|
"lanes-watch": [
|
|
140
191
|
async () => {
|
|
@@ -144,6 +195,9 @@ const LOADERS = {
|
|
|
144
195
|
() => import("../lib/plan-limit-nag.js"),
|
|
145
196
|
() => import("../utils/plan-gate-error.js"),
|
|
146
197
|
() => import("open"),
|
|
198
|
+
() => import("../utils/cognito-session.js"),
|
|
199
|
+
() => import("../lib/first-sync-next-step.js"),
|
|
200
|
+
() => import("@indigoai-us/hq-cloud"),
|
|
147
201
|
],
|
|
148
202
|
"monitor-wait": [
|
|
149
203
|
async () => {
|
|
@@ -153,6 +207,9 @@ const LOADERS = {
|
|
|
153
207
|
() => import("../lib/plan-limit-nag.js"),
|
|
154
208
|
() => import("../utils/plan-gate-error.js"),
|
|
155
209
|
() => import("open"),
|
|
210
|
+
() => import("../utils/cognito-session.js"),
|
|
211
|
+
() => import("../lib/first-sync-next-step.js"),
|
|
212
|
+
() => import("@indigoai-us/hq-cloud"),
|
|
156
213
|
],
|
|
157
214
|
"monitor-run": [
|
|
158
215
|
() => import("../commands/lanes.js"),
|
|
@@ -163,6 +220,33 @@ const LOADERS = {
|
|
|
163
220
|
preloadSentrySdk();
|
|
164
221
|
},
|
|
165
222
|
() => import("open"),
|
|
223
|
+
() => import("../utils/cognito-session.js"),
|
|
224
|
+
() => import("../lib/first-sync-next-step.js"),
|
|
225
|
+
() => import("@indigoai-us/hq-cloud"),
|
|
226
|
+
],
|
|
227
|
+
"agents-message-wait": [
|
|
228
|
+
async () => {
|
|
229
|
+
const { preloadSentrySdk } = await import("../sentry.js");
|
|
230
|
+
preloadSentrySdk();
|
|
231
|
+
},
|
|
232
|
+
() => import("../lib/plan-limit-nag.js"),
|
|
233
|
+
() => import("../utils/plan-gate-error.js"),
|
|
234
|
+
() => import("open"),
|
|
235
|
+
() => import("../utils/cognito-session.js"),
|
|
236
|
+
() => import("../lib/first-sync-next-step.js"),
|
|
237
|
+
() => import("@indigoai-us/hq-cloud"),
|
|
238
|
+
],
|
|
239
|
+
"integrations-oauth-wait": [
|
|
240
|
+
async () => {
|
|
241
|
+
const { preloadSentrySdk } = await import("../sentry.js");
|
|
242
|
+
preloadSentrySdk();
|
|
243
|
+
},
|
|
244
|
+
() => import("../lib/plan-limit-nag.js"),
|
|
245
|
+
() => import("../utils/plan-gate-error.js"),
|
|
246
|
+
() => import("open"),
|
|
247
|
+
() => import("../utils/cognito-session.js"),
|
|
248
|
+
() => import("../lib/first-sync-next-step.js"),
|
|
249
|
+
() => import("@indigoai-us/hq-cloud"),
|
|
166
250
|
],
|
|
167
251
|
};
|
|
168
252
|
export class ResidentPackagePreloadError extends Error {
|