@indigoai-us/hq-cli 5.202.1 → 5.204.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 +15 -0
- package/README.md +24 -0
- package/assets/lanes-hooks/hq-lanes-drain.mjs +36 -15
- package/dist/command-catalog.generated.d.ts +195 -0
- package/dist/command-catalog.generated.js +249 -0
- package/dist/command-registration-plan.d.ts +6 -0
- package/dist/command-registration-plan.js +1 -0
- package/dist/commands/lanes.d.ts +4 -0
- package/dist/commands/lanes.js +350 -108
- package/dist/commands/monitor.d.ts +58 -0
- package/dist/commands/monitor.js +965 -0
- package/dist/lib/lanes/account-home.d.ts +8 -0
- package/dist/lib/lanes/account-home.js +10 -0
- package/dist/lib/lanes/accounts.d.ts +4 -1
- package/dist/lib/lanes/accounts.js +15 -10
- package/dist/lib/lanes/claude-account-balance.d.ts +3 -0
- package/dist/lib/lanes/claude-account-balance.js +19 -4
- package/dist/lib/lanes/config.d.ts +2 -1
- package/dist/lib/lanes/config.js +55 -13
- package/dist/lib/lanes/delivery-core.d.ts +15 -0
- package/dist/lib/lanes/delivery-core.js +21 -0
- package/dist/lib/lanes/delivery.d.ts +21 -5
- package/dist/lib/lanes/delivery.js +71 -7
- package/dist/lib/lanes/dropbox.d.ts +13 -1
- package/dist/lib/lanes/dropbox.js +61 -5
- package/dist/lib/lanes/host-capacity.d.ts +4 -0
- package/dist/lib/lanes/host-capacity.js +8 -1
- package/dist/lib/lanes/index.d.ts +3 -1
- package/dist/lib/lanes/index.js +2 -1
- package/dist/lib/lanes/liveness.d.ts +1 -0
- package/dist/lib/lanes/liveness.js +3 -1
- package/dist/lib/lanes/move-account.js +1 -1
- package/dist/lib/lanes/reconcile.js +20 -9
- package/dist/lib/lanes/spawn.d.ts +7 -3
- package/dist/lib/lanes/spawn.js +71 -22
- package/dist/lib/lanes/types.d.ts +8 -0
- package/dist/lib/monitor.d.ts +153 -0
- package/dist/lib/monitor.js +519 -0
- package/dist/main.js +40 -28
- package/dist/utils/cli-network-lifecycle.d.ts +4 -0
- package/dist/utils/cli-network-lifecycle.js +11 -0
- package/package.json +1 -1
package/dist/lib/lanes/spawn.js
CHANGED
|
@@ -12,6 +12,7 @@ import * as fs from "node:fs";
|
|
|
12
12
|
import * as path from "node:path";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
14
|
import { PROVIDER_HOME_ENV, accountStatus, isLoggedOut, resolveAccount, selectBalancedAccount, } from "./accounts.js";
|
|
15
|
+
import { effectiveAccountHome } from "./account-home.js";
|
|
15
16
|
import { COMPACTION_PROMPT, compactionAfterUsage, compactionDue, compactLaneSession, } from "./compaction.js";
|
|
16
17
|
import { installDrainHook } from "./hooks-install.js";
|
|
17
18
|
import { loadLanesConfig, providerAutoCompactTokens, providerCompactAfterMs, } from "./config.js";
|
|
@@ -112,7 +113,7 @@ function accountModelForSpawn(provider, accountId, config, pinnedModel, profileM
|
|
|
112
113
|
providerModel: config.providers[provider].model,
|
|
113
114
|
});
|
|
114
115
|
}
|
|
115
|
-
function supportsSpawnModel(provider, accountId, config, pinnedModel, profileModel, modelDeps) {
|
|
116
|
+
function supportsSpawnModel(provider, accountId, config, pinnedModel, profileModel, modelDeps, hostname) {
|
|
116
117
|
const account = config.providers[provider].accounts[accountId];
|
|
117
118
|
if (account === undefined)
|
|
118
119
|
return false;
|
|
@@ -120,7 +121,7 @@ function supportsSpawnModel(provider, accountId, config, pinnedModel, profileMod
|
|
|
120
121
|
if (model === undefined)
|
|
121
122
|
return true;
|
|
122
123
|
try {
|
|
123
|
-
validateLaneModel(provider, model, account.home, modelDeps);
|
|
124
|
+
validateLaneModel(provider, model, effectiveAccountHome(account, hostname).home, modelDeps);
|
|
124
125
|
return true;
|
|
125
126
|
}
|
|
126
127
|
catch (err) {
|
|
@@ -129,7 +130,7 @@ function supportsSpawnModel(provider, accountId, config, pinnedModel, profileMod
|
|
|
129
130
|
throw err;
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
|
-
function spawnModelFailure(provider, accountId, config, pinnedModel, profileModel, profileId, modelDeps) {
|
|
133
|
+
function spawnModelFailure(provider, accountId, config, pinnedModel, profileModel, profileId, modelDeps, hostname) {
|
|
133
134
|
const account = config.providers[provider].accounts[accountId];
|
|
134
135
|
if (account === undefined)
|
|
135
136
|
return new LanesError("unknown_account", `Unknown account ${accountId} for ${provider}`, { provider, account: accountId });
|
|
@@ -137,7 +138,7 @@ function spawnModelFailure(provider, accountId, config, pinnedModel, profileMode
|
|
|
137
138
|
if (model === undefined)
|
|
138
139
|
return null;
|
|
139
140
|
try {
|
|
140
|
-
validateLaneModel(provider, model, account.home, modelDeps);
|
|
141
|
+
validateLaneModel(provider, model, effectiveAccountHome(account, hostname).home, modelDeps);
|
|
141
142
|
return null;
|
|
142
143
|
}
|
|
143
144
|
catch (err) {
|
|
@@ -155,17 +156,18 @@ function spawnModelFailure(provider, accountId, config, pinnedModel, profileMode
|
|
|
155
156
|
}
|
|
156
157
|
}
|
|
157
158
|
function selectSpawnAccount(input) {
|
|
158
|
-
const { hqRoot, lane, config, existsSync, accountDeps, modelDeps, pinnedModel, profileModel, currentModelValidated } = input;
|
|
159
|
+
const { hqRoot, lane, config, existsSync, accountDeps, modelDeps, pinnedModel, profileModel, currentModelValidated, hostname } = input;
|
|
159
160
|
const context = {
|
|
160
161
|
hqRoot,
|
|
161
162
|
existsSync,
|
|
162
163
|
accountDeps,
|
|
164
|
+
hostname,
|
|
163
165
|
...(pinnedModel ?? profileModel ? { model: pinnedModel ?? profileModel } : {}),
|
|
164
166
|
};
|
|
165
167
|
if (lane.account === undefined) {
|
|
166
168
|
const modelSpecified = pinnedModel !== undefined || profileModel !== undefined;
|
|
167
169
|
const allowedAccounts = modelSpecified
|
|
168
|
-
? Object.keys(config.providers[lane.provider].accounts).filter((id) => supportsSpawnModel(lane.provider, id, config, pinnedModel, profileModel, modelDeps))
|
|
170
|
+
? Object.keys(config.providers[lane.provider].accounts).filter((id) => supportsSpawnModel(lane.provider, id, config, pinnedModel, profileModel, modelDeps, hostname))
|
|
169
171
|
: undefined;
|
|
170
172
|
return {
|
|
171
173
|
account: selectBalancedAccount(lane.provider, config, {
|
|
@@ -188,6 +190,7 @@ function selectSpawnAccount(input) {
|
|
|
188
190
|
else {
|
|
189
191
|
const status = accountStatus(lane.provider, lane.account, config, {
|
|
190
192
|
existsSync,
|
|
193
|
+
hostname,
|
|
191
194
|
...(accountDeps?.probe ? { probe: accountDeps.probe } : {}),
|
|
192
195
|
...(accountDeps?.env ? { env: accountDeps.env } : {}),
|
|
193
196
|
});
|
|
@@ -200,7 +203,7 @@ function selectSpawnAccount(input) {
|
|
|
200
203
|
resolveAccount(lane.provider, lane.account, config, context);
|
|
201
204
|
const modelFailure = currentModelValidated
|
|
202
205
|
? null
|
|
203
|
-
: spawnModelFailure(lane.provider, lane.account, config, pinnedModel, profileModel, lane.worker_profile, modelDeps);
|
|
206
|
+
: spawnModelFailure(lane.provider, lane.account, config, pinnedModel, profileModel, lane.worker_profile, modelDeps, hostname);
|
|
204
207
|
if (modelFailure !== null) {
|
|
205
208
|
reason = "model_change";
|
|
206
209
|
refusal = modelFailure;
|
|
@@ -240,10 +243,11 @@ function selectSpawnAccount(input) {
|
|
|
240
243
|
.filter(([id, account]) => id !== lane.account && account.enabled)
|
|
241
244
|
.filter(([id]) => accountStatus(lane.provider, id, config, {
|
|
242
245
|
existsSync,
|
|
246
|
+
hostname,
|
|
243
247
|
...(accountDeps?.probe ? { probe: accountDeps.probe } : {}),
|
|
244
248
|
...(accountDeps?.env ? { env: accountDeps.env } : {}),
|
|
245
249
|
}).logged_in === true)
|
|
246
|
-
.filter(([id]) => supportsSpawnModel(lane.provider, id, config, pinnedModel, profileModel, modelDeps))
|
|
250
|
+
.filter(([id]) => supportsSpawnModel(lane.provider, id, config, pinnedModel, profileModel, modelDeps, hostname))
|
|
247
251
|
.map(([id]) => id);
|
|
248
252
|
try {
|
|
249
253
|
return {
|
|
@@ -345,6 +349,8 @@ export async function retryLaneOnAlternateAccount(hqRoot, lane, reason, writer =
|
|
|
345
349
|
const config = loadLanesConfig(hqRoot);
|
|
346
350
|
const existsSync = deps.existsSync ?? ((p) => fs.existsSync(p));
|
|
347
351
|
const env = deps.env ?? process.env;
|
|
352
|
+
const admissionHost = deps.admissionHost ?? resolveAdmissionHost();
|
|
353
|
+
const hostname = deps.hostname?.trim() || admissionHost.hostname.trim() || resolveAdmissionHost().hostname;
|
|
348
354
|
const modelDeps = {
|
|
349
355
|
...deps.modelDeps,
|
|
350
356
|
env: deps.modelDeps?.env ?? env,
|
|
@@ -353,7 +359,6 @@ export async function retryLaneOnAlternateAccount(hqRoot, lane, reason, writer =
|
|
|
353
359
|
probe: modelDeps.probe ?? defaultProbe,
|
|
354
360
|
env,
|
|
355
361
|
};
|
|
356
|
-
const admissionHost = deps.admissionHost ?? resolveAdmissionHost();
|
|
357
362
|
const profile = lane.worker_profile ? loadWorkerProfile(hqRoot, lane.worker_profile) : undefined;
|
|
358
363
|
const profileModel = profile?.meta.provider === lane.provider ? profile.meta.model : undefined;
|
|
359
364
|
const pinnedModel = lane.model_source === undefined || lane.model_source === "cli" ? lane.model : undefined;
|
|
@@ -362,15 +367,17 @@ export async function retryLaneOnAlternateAccount(hqRoot, lane, reason, writer =
|
|
|
362
367
|
.filter(([id, account]) => id !== lane.account && !unavailableAccounts.has(id) && account.enabled)
|
|
363
368
|
.filter(([id]) => accountStatus(lane.provider, id, config, {
|
|
364
369
|
existsSync,
|
|
370
|
+
hostname,
|
|
365
371
|
...(accountDeps?.probe ? { probe: accountDeps.probe } : {}),
|
|
366
372
|
...(accountDeps?.env ? { env: accountDeps.env } : {}),
|
|
367
373
|
}).logged_in === true)
|
|
368
|
-
.filter(([id]) => supportsSpawnModel(lane.provider, id, config, pinnedModel, profileModel, modelDeps))
|
|
374
|
+
.filter(([id]) => supportsSpawnModel(lane.provider, id, config, pinnedModel, profileModel, modelDeps, hostname))
|
|
369
375
|
.map(([id]) => id);
|
|
370
376
|
const modelContext = {
|
|
371
377
|
hqRoot,
|
|
372
378
|
existsSync,
|
|
373
379
|
accountDeps,
|
|
380
|
+
hostname,
|
|
374
381
|
...(pinnedModel ?? profileModel ? { model: pinnedModel ?? profileModel } : {}),
|
|
375
382
|
allowedAccounts,
|
|
376
383
|
};
|
|
@@ -392,7 +399,7 @@ export async function retryLaneOnAlternateAccount(hqRoot, lane, reason, writer =
|
|
|
392
399
|
: undefined;
|
|
393
400
|
if (source && sessionId) {
|
|
394
401
|
copySessionFiles(planSessionCopy(lane.provider, {
|
|
395
|
-
sourceHome: source.home,
|
|
402
|
+
sourceHome: lane.provider_home_at_spawn ?? effectiveAccountHome(source, hostname).home,
|
|
396
403
|
targetHome: target.home,
|
|
397
404
|
cwd: hqRoot,
|
|
398
405
|
sessionId,
|
|
@@ -535,6 +542,11 @@ function appendCodexWorkerPolicyDirFlag(provider, args, workerPolicyDir) {
|
|
|
535
542
|
return [...args, "-c", setting];
|
|
536
543
|
return [...args.slice(0, separator), "-c", setting, ...args.slice(separator)];
|
|
537
544
|
}
|
|
545
|
+
function accountHomeHostname(deps) {
|
|
546
|
+
return (deps.hostname?.trim() ||
|
|
547
|
+
deps.admissionHost?.hostname.trim() ||
|
|
548
|
+
resolveAdmissionHost().hostname);
|
|
549
|
+
}
|
|
538
550
|
export const PID_LIVENESS_DECISIONS = [
|
|
539
551
|
"esrch",
|
|
540
552
|
"starttime_mismatch",
|
|
@@ -995,7 +1007,10 @@ export function resumePrompt(input) {
|
|
|
995
1007
|
? "The previous run ended without writing its envelope; continue the work from where it stopped."
|
|
996
1008
|
: "Continue the work from where the previous run stopped."));
|
|
997
1009
|
}
|
|
998
|
-
if (input.
|
|
1010
|
+
if (input.monitorEvent !== undefined) {
|
|
1011
|
+
parts.push(input.monitorEvent);
|
|
1012
|
+
}
|
|
1013
|
+
else if (input.operatorMessage !== undefined) {
|
|
999
1014
|
parts.push("OPERATOR INSTRUCTION (outranks the lane brief). " +
|
|
1000
1015
|
"The following message was sent to this lane by the operator. Treat it as an operator instruction that outranks the brief.\n\n" +
|
|
1001
1016
|
input.operatorMessage);
|
|
@@ -1022,6 +1037,7 @@ export function resumePrompt(input) {
|
|
|
1022
1037
|
// Every resume turn carries the authorization, including a textless
|
|
1023
1038
|
// `hq lanes resume`: a pre-upgrade session's brief never had the paragraph.
|
|
1024
1039
|
if (input.operatorMessage !== undefined ||
|
|
1040
|
+
input.monitorEvent !== undefined ||
|
|
1025
1041
|
input.answersPath !== undefined ||
|
|
1026
1042
|
input.resumedFrom !== undefined) {
|
|
1027
1043
|
parts.push(STANDING_AUTHORIZATION);
|
|
@@ -1097,6 +1113,21 @@ export function buildProviderCommand(provider, input) {
|
|
|
1097
1113
|
input.resume.prompt,
|
|
1098
1114
|
];
|
|
1099
1115
|
}
|
|
1116
|
+
else if (input.resume && parsed === "grok") {
|
|
1117
|
+
// `grok --single <prompt> --resume <id>` (1.0.34) continues the recorded
|
|
1118
|
+
// session and prints the same `sessionId` in its JSON result.
|
|
1119
|
+
args = [
|
|
1120
|
+
"--single",
|
|
1121
|
+
input.resume.prompt,
|
|
1122
|
+
"--resume",
|
|
1123
|
+
input.resume.session_id,
|
|
1124
|
+
"--permission-mode",
|
|
1125
|
+
"bypassPermissions",
|
|
1126
|
+
"--always-approve",
|
|
1127
|
+
"--output-format",
|
|
1128
|
+
"json",
|
|
1129
|
+
];
|
|
1130
|
+
}
|
|
1100
1131
|
else if (input.resume) {
|
|
1101
1132
|
args = [
|
|
1102
1133
|
"-p",
|
|
@@ -1508,6 +1539,7 @@ function inferTrigger(lane) {
|
|
|
1508
1539
|
*/
|
|
1509
1540
|
export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
1510
1541
|
const env = deps.env ?? process.env;
|
|
1542
|
+
const hostname = accountHomeHostname(deps);
|
|
1511
1543
|
const modelDeps = {
|
|
1512
1544
|
...deps.modelDeps,
|
|
1513
1545
|
env: deps.modelDeps?.env ?? env,
|
|
@@ -1625,6 +1657,7 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
1625
1657
|
pinnedModel,
|
|
1626
1658
|
profileModel,
|
|
1627
1659
|
currentModelValidated: deps.currentModelValidated === true,
|
|
1660
|
+
hostname,
|
|
1628
1661
|
});
|
|
1629
1662
|
}
|
|
1630
1663
|
catch (err) {
|
|
@@ -1640,7 +1673,7 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
1640
1673
|
throw err;
|
|
1641
1674
|
}
|
|
1642
1675
|
const resolved = selection.account;
|
|
1643
|
-
const status = accountStatus(lane.provider, resolved.id, config, { existsSync });
|
|
1676
|
+
const status = accountStatus(lane.provider, resolved.id, config, { existsSync, hostname });
|
|
1644
1677
|
if (isLoggedOut(status)) {
|
|
1645
1678
|
const failed = markPreSpawnFailure
|
|
1646
1679
|
? markSpawnFailure(hqRoot, lane.lane_id, "account_not_logged_in", {
|
|
@@ -1658,7 +1691,7 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
1658
1691
|
existing.provider_session_id &&
|
|
1659
1692
|
providerSupportsSessionResume(existing.provider)) {
|
|
1660
1693
|
copySessionFiles(planSessionCopy(existing.provider, {
|
|
1661
|
-
sourceHome: source.home,
|
|
1694
|
+
sourceHome: existing.provider_home_at_spawn ?? effectiveAccountHome(source, hostname).home,
|
|
1662
1695
|
targetHome: resolved.home,
|
|
1663
1696
|
cwd: hqRoot,
|
|
1664
1697
|
sessionId: existing.provider_session_id,
|
|
@@ -1672,7 +1705,7 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
1672
1705
|
cwd: hqRoot,
|
|
1673
1706
|
targetHome: resolved.home,
|
|
1674
1707
|
registeredHomes: Object.values(config.providers[existing.provider].accounts)
|
|
1675
|
-
.map((account) => account.home),
|
|
1708
|
+
.map((account) => effectiveAccountHome(account, hostname).home),
|
|
1676
1709
|
providerHomeAtSpawn: existing.provider_home_at_spawn,
|
|
1677
1710
|
});
|
|
1678
1711
|
}
|
|
@@ -1745,11 +1778,18 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
1745
1778
|
session_id: plan.session_id,
|
|
1746
1779
|
prompt: resumePrompt({
|
|
1747
1780
|
envelopePath,
|
|
1748
|
-
operatorMessage: trigger.kind === "message"
|
|
1781
|
+
operatorMessage: trigger.kind === "message" && trigger.monitor_event !== true
|
|
1749
1782
|
? trigger.text
|
|
1750
1783
|
: trigger.kind === "resume"
|
|
1751
1784
|
? trigger.text
|
|
1752
|
-
: pendingResume?.
|
|
1785
|
+
: pendingResume?.monitor_event === true
|
|
1786
|
+
? undefined
|
|
1787
|
+
: pendingResume?.text,
|
|
1788
|
+
monitorEvent: trigger.kind === "message" && trigger.monitor_event === true
|
|
1789
|
+
? trigger.text
|
|
1790
|
+
: pendingResume?.monitor_event === true
|
|
1791
|
+
? pendingResume.text
|
|
1792
|
+
: undefined,
|
|
1753
1793
|
resumedFrom: trigger.kind === "resume" ? trigger.from : pendingResume?.resumed_from,
|
|
1754
1794
|
answersPath: (trigger.kind === "answered_questions" || pendingResume?.trigger === "answered_questions") && existsSync(answersPath)
|
|
1755
1795
|
? answersPath
|
|
@@ -1933,6 +1973,7 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
1933
1973
|
account: resolved.id,
|
|
1934
1974
|
trigger: pendingResume.trigger,
|
|
1935
1975
|
...(pendingResume.message_id !== undefined ? { message_id: pendingResume.message_id } : {}),
|
|
1976
|
+
...(pendingResume.monitor_event === true ? { monitor_event: true } : {}),
|
|
1936
1977
|
...(pendingResume.resumed_from !== undefined ? { resumed_from: pendingResume.resumed_from } : {}),
|
|
1937
1978
|
...(pendingResume.text !== undefined ? { text: pendingResume.text } : {}),
|
|
1938
1979
|
...(pendingResume.compact !== undefined ? { compact: pendingResume.compact } : {}),
|
|
@@ -1945,7 +1986,11 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
1945
1986
|
account: resolved.id,
|
|
1946
1987
|
trigger: trigger.kind,
|
|
1947
1988
|
...(trigger.kind === "message"
|
|
1948
|
-
? {
|
|
1989
|
+
? {
|
|
1990
|
+
message_id: trigger.message_id,
|
|
1991
|
+
text: trigger.text,
|
|
1992
|
+
...(trigger.monitor_event === true ? { monitor_event: true } : {}),
|
|
1993
|
+
}
|
|
1949
1994
|
: {}),
|
|
1950
1995
|
...(launch.compact !== undefined ? { compact: launch.compact } : {}),
|
|
1951
1996
|
...(trigger.kind === "resume"
|
|
@@ -2043,12 +2088,14 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
2043
2088
|
}
|
|
2044
2089
|
/**
|
|
2045
2090
|
* `resume`: claude and codex continue the recorded session with the repair
|
|
2046
|
-
* prompt. `fresh_turn`: grok
|
|
2047
|
-
*
|
|
2048
|
-
*
|
|
2091
|
+
* prompt. `fresh_turn`: grok gets a new single-turn invocation on the same
|
|
2092
|
+
* account, whose prompt carries the schema, the parser error and the raw
|
|
2093
|
+
* failed output. Grok can resume a session (operator messages and
|
|
2094
|
+
* `hq lanes resume` use it), but its repair turn stays a fresh turn: that
|
|
2095
|
+
* path was built and proven that way, and a repair by resume has not been.
|
|
2049
2096
|
*/
|
|
2050
2097
|
export function envelopeRepairMode(provider) {
|
|
2051
|
-
return providerSupportsSessionResume(provider) ? "resume" : "fresh_turn";
|
|
2098
|
+
return providerSupportsSessionResume(provider) && provider !== "grok" ? "resume" : "fresh_turn";
|
|
2052
2099
|
}
|
|
2053
2100
|
/**
|
|
2054
2101
|
* Spec §4 repair turn. The lane is still `running` (the watcher has not
|
|
@@ -2062,6 +2109,7 @@ export function envelopeRepairMode(provider) {
|
|
|
2062
2109
|
*/
|
|
2063
2110
|
export function spawnEnvelopeRepair(hqRoot, lane, input, deps = {}) {
|
|
2064
2111
|
const env = deps.env ?? process.env;
|
|
2112
|
+
const hostname = accountHomeHostname(deps);
|
|
2065
2113
|
const existsSync = deps.existsSync ?? ((p) => fs.existsSync(p));
|
|
2066
2114
|
const spawnProcess = deps.spawnProcess ?? defaultSpawnProcess;
|
|
2067
2115
|
const proveDetached = deps.proveDetached ?? defaultProveDetached;
|
|
@@ -2108,6 +2156,7 @@ export function spawnEnvelopeRepair(hqRoot, lane, input, deps = {}) {
|
|
|
2108
2156
|
const resolved = resolveAccount(lane.provider, lane.account, config, {
|
|
2109
2157
|
hqRoot,
|
|
2110
2158
|
existsSync,
|
|
2159
|
+
hostname,
|
|
2111
2160
|
model: pinnedModel ?? profileModel,
|
|
2112
2161
|
});
|
|
2113
2162
|
if (profileDefaultsApply && profileModel !== undefined && pinnedModel === undefined) {
|
|
@@ -514,6 +514,8 @@ export interface PendingResume {
|
|
|
514
514
|
resumed_from?: "interrupted" | "done" | "blocked" | "failed";
|
|
515
515
|
text?: string;
|
|
516
516
|
message_id?: string;
|
|
517
|
+
/** The text is already framed as untrusted monitor data, not an operator instruction. */
|
|
518
|
+
monitor_event?: true;
|
|
517
519
|
/** False when the command passed --no-compact; the deferred spawn honours it. */
|
|
518
520
|
compact?: boolean;
|
|
519
521
|
at: string;
|
|
@@ -534,6 +536,7 @@ export interface LaneResumeRecord {
|
|
|
534
536
|
account?: string;
|
|
535
537
|
trigger: "answered_questions" | "message" | "resume";
|
|
536
538
|
message_id?: string;
|
|
539
|
+
monitor_event?: true;
|
|
537
540
|
/**
|
|
538
541
|
* `hq lanes resume` only: the state the lane was in when the operator
|
|
539
542
|
* resumed it (`interrupted`, `done` or `blocked`) and the text they sent.
|
|
@@ -642,6 +645,8 @@ export interface LaneEnvelope {
|
|
|
642
645
|
}
|
|
643
646
|
export interface AccountConfig {
|
|
644
647
|
home: string;
|
|
648
|
+
/** Optional account home overrides keyed by the owning host's hostname. */
|
|
649
|
+
hosts?: Record<string, AccountHostHome>;
|
|
645
650
|
enabled: boolean;
|
|
646
651
|
disabled_reason?: string;
|
|
647
652
|
max_running?: number;
|
|
@@ -650,6 +655,9 @@ export interface AccountConfig {
|
|
|
650
655
|
/** Default effort for this account. Beats the provider default; beaten by --effort. */
|
|
651
656
|
effort?: string;
|
|
652
657
|
}
|
|
658
|
+
export interface AccountHostHome {
|
|
659
|
+
home: string;
|
|
660
|
+
}
|
|
653
661
|
export interface ProviderConfig {
|
|
654
662
|
max_running: number;
|
|
655
663
|
max_queued: number;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { LaneCompanyRef, LaneProvider } from "./lanes/types.js";
|
|
2
|
+
export declare const MONITOR_SCHEMA_VERSION: "hq-monitor.v1";
|
|
3
|
+
export declare const MONITOR_FLAG = "monitor.enabled";
|
|
4
|
+
export declare const MONITOR_FLAG_DEFAULT = false;
|
|
5
|
+
export declare const MONITOR_DEFAULT_TIMEOUT_MS: number;
|
|
6
|
+
export declare const MONITOR_DEFAULT_CHECKIN_MS: number;
|
|
7
|
+
export declare const MONITOR_PROMPT_CACHE_TTL_MS: number;
|
|
8
|
+
export declare const MONITOR_BATCH_WINDOW_MS = 200;
|
|
9
|
+
export declare const MONITOR_POLL_INTERVAL_MS = 1000;
|
|
10
|
+
export declare const MONITOR_RATE_WINDOW_MS = 10000;
|
|
11
|
+
/** Sustained stdout floods stop after 3 windows; the per-window cap can be overridden with HQ_MONITOR_MAX_EVENTS_PER_WINDOW. */
|
|
12
|
+
export declare const MONITOR_MAX_EVENTS_PER_WINDOW = 60;
|
|
13
|
+
export declare const MONITOR_MAX_FLOOD_WINDOWS = 3;
|
|
14
|
+
export declare const MONITOR_LOCK_STALE_MS = 30000;
|
|
15
|
+
export declare const MONITOR_MAX_TIMER_DELAY_MS = 2147000000;
|
|
16
|
+
export type MonitorTarget = {
|
|
17
|
+
kind: "lane";
|
|
18
|
+
laneId: string;
|
|
19
|
+
} | {
|
|
20
|
+
kind: "session";
|
|
21
|
+
provider: LaneProvider;
|
|
22
|
+
sessionId: string;
|
|
23
|
+
};
|
|
24
|
+
export interface MonitorProcessEntry {
|
|
25
|
+
pid: number;
|
|
26
|
+
ppid: number;
|
|
27
|
+
command: string;
|
|
28
|
+
}
|
|
29
|
+
export interface DetectMonitorTargetOptions {
|
|
30
|
+
env: NodeJS.ProcessEnv;
|
|
31
|
+
parentPid: number;
|
|
32
|
+
readProcess: (pid: number) => MonitorProcessEntry | null;
|
|
33
|
+
}
|
|
34
|
+
export interface NearestMonitorRuntime {
|
|
35
|
+
provider: LaneProvider;
|
|
36
|
+
pid: number;
|
|
37
|
+
}
|
|
38
|
+
export interface MonitorRuntimeIdentity extends NearestMonitorRuntime {
|
|
39
|
+
starttime: number;
|
|
40
|
+
}
|
|
41
|
+
export interface MonitorSessionOwner {
|
|
42
|
+
pid: number;
|
|
43
|
+
starttime: number | null;
|
|
44
|
+
}
|
|
45
|
+
export interface MonitorMetadata {
|
|
46
|
+
schema_version: typeof MONITOR_SCHEMA_VERSION;
|
|
47
|
+
monitor_id: string;
|
|
48
|
+
target: MonitorTarget;
|
|
49
|
+
target_key: string;
|
|
50
|
+
company: LaneCompanyRef | null;
|
|
51
|
+
description: string;
|
|
52
|
+
command: string | null;
|
|
53
|
+
argv: string[] | null;
|
|
54
|
+
cwd: string;
|
|
55
|
+
created_at: string;
|
|
56
|
+
timeout_ms: number | null;
|
|
57
|
+
persistent: boolean;
|
|
58
|
+
checkin_ms: number | null;
|
|
59
|
+
session_owner: MonitorSessionOwner | null;
|
|
60
|
+
}
|
|
61
|
+
export interface MonitorState {
|
|
62
|
+
status: "starting" | "running" | "stopping" | "terminal";
|
|
63
|
+
pid: number | null;
|
|
64
|
+
starttime: number | null;
|
|
65
|
+
child_pid?: number | null;
|
|
66
|
+
child_starttime?: number | null;
|
|
67
|
+
started_at?: string;
|
|
68
|
+
finished_at?: string;
|
|
69
|
+
exit_code?: number | null;
|
|
70
|
+
terminal_reason?: string;
|
|
71
|
+
event_count: number;
|
|
72
|
+
/** Last sequence written to events.jsonl, including undeliverable events. */
|
|
73
|
+
event_sequence?: number;
|
|
74
|
+
}
|
|
75
|
+
export interface MonitorEvent {
|
|
76
|
+
seq: number;
|
|
77
|
+
at: string;
|
|
78
|
+
text: string;
|
|
79
|
+
delivery: string;
|
|
80
|
+
reason?: string;
|
|
81
|
+
}
|
|
82
|
+
export interface MonitorTargetActivity {
|
|
83
|
+
last_delivery_ms: number;
|
|
84
|
+
last_delivery_kind: "monitor_event" | "checkin" | "start";
|
|
85
|
+
checkin_count: number;
|
|
86
|
+
last_activity_ms?: number;
|
|
87
|
+
last_checkin_attempt_ms?: number;
|
|
88
|
+
}
|
|
89
|
+
export interface MonitorCheckinDeps {
|
|
90
|
+
now: () => Date;
|
|
91
|
+
isWakeable: (targetKey: string) => boolean | Promise<boolean>;
|
|
92
|
+
readTargetActivityAt?: (targetKey: string) => number | null | Promise<number | null>;
|
|
93
|
+
deliver: (text: string, sequence: number) => Promise<{
|
|
94
|
+
delivered: boolean;
|
|
95
|
+
outcome: string;
|
|
96
|
+
}>;
|
|
97
|
+
pidIsAlive: (pid: number, starttime: number | null) => boolean;
|
|
98
|
+
readStarttime: (pid: number) => number | null;
|
|
99
|
+
sleep?: (ms: number) => Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
export interface MonitorCheckinResult {
|
|
102
|
+
sent: boolean;
|
|
103
|
+
sequence: number | null;
|
|
104
|
+
text: string | null;
|
|
105
|
+
outcome: string | null;
|
|
106
|
+
}
|
|
107
|
+
/** This guard is first in monitor start so a conflicting invocation has no side effects. */
|
|
108
|
+
export declare function assertMonitorTimeoutCompatibility(persistent: boolean, timeoutProvided: boolean): void;
|
|
109
|
+
export declare function parseMonitorDuration(value: string, kind: "timeout" | "checkin"): number;
|
|
110
|
+
export declare function assertMonitorSessionId(sessionId: string): void;
|
|
111
|
+
export declare function parseMonitorTarget(value: string): MonitorTarget;
|
|
112
|
+
export declare function monitorTargetKey(target: MonitorTarget): string;
|
|
113
|
+
export declare function detectMonitorTarget(options: DetectMonitorTargetOptions): MonitorTarget;
|
|
114
|
+
/** The nearest runtime process in the caller chain, shared by target detection and hook mapping. */
|
|
115
|
+
export declare function findNearestMonitorRuntime(options: {
|
|
116
|
+
parentPid: number;
|
|
117
|
+
readProcess: (pid: number) => MonitorProcessEntry | null;
|
|
118
|
+
}): NearestMonitorRuntime | null;
|
|
119
|
+
export declare function monitorSessionDir(hqRoot: string, provider: LaneProvider, sessionId: string): string;
|
|
120
|
+
export declare function monitorSessionHasActiveMarkers(hqRoot: string, provider: LaneProvider, sessionId: string): boolean;
|
|
121
|
+
export declare function monitorSessionHasActiveMarkerForOwner(hqRoot: string, provider: LaneProvider, sessionId: string, owner: MonitorRuntimeIdentity): boolean;
|
|
122
|
+
export declare function monitorSessionHasInboxFile(hqRoot: string, provider: LaneProvider, sessionId: string): boolean;
|
|
123
|
+
export declare function monitorSessionInboxHasEvents(hqRoot: string, provider: LaneProvider, sessionId: string): boolean;
|
|
124
|
+
export declare function monitorSessionHasWork(hqRoot: string, provider: LaneProvider, sessionId: string): boolean;
|
|
125
|
+
export declare function monitorDir(hqRoot: string, monitorId: string): string;
|
|
126
|
+
export declare function makeMonitorId(): string;
|
|
127
|
+
export declare function formatMonitorEvent(input: {
|
|
128
|
+
monitorId: string;
|
|
129
|
+
description: string;
|
|
130
|
+
sequence: number;
|
|
131
|
+
text: string;
|
|
132
|
+
}): string;
|
|
133
|
+
export declare function nextMonitorEventSequence(state: Pick<MonitorState, "event_sequence">): number;
|
|
134
|
+
export declare function nextMonitorTimeoutDelay(timeoutMs: number, elapsedMs: number): number;
|
|
135
|
+
export declare function recordMonitorEventState(state: MonitorState, event: MonitorEvent): MonitorState;
|
|
136
|
+
/** Keep the supervisor alive until its command has closed so TERM escalation can run. */
|
|
137
|
+
export declare function requestMonitorStop(childPid: number | null, terminate: () => void, finishWithoutChild: () => void): void;
|
|
138
|
+
export declare function parseHookSessionId(payloadText: string): string;
|
|
139
|
+
export declare function recordHookSessionMapping(hqRoot: string, provider: LaneProvider, environmentSessionId: string | undefined, payloadSessionId: string, runtime: MonitorRuntimeIdentity | undefined): void;
|
|
140
|
+
export declare function resolveMappedSessionId(hqRoot: string, provider: LaneProvider, environmentSessionId: string, runtime: MonitorRuntimeIdentity | undefined): string;
|
|
141
|
+
export declare function withMonitorTargetLock<T>(hqRoot: string, targetKey: string, operation: (activityPath: string) => Promise<T> | T, options?: {
|
|
142
|
+
now?: () => Date;
|
|
143
|
+
pidIsAlive?: (pid: number, starttime: number | null) => boolean;
|
|
144
|
+
sleep?: (ms: number) => Promise<void>;
|
|
145
|
+
}): Promise<T>;
|
|
146
|
+
export declare function recordMonitorTargetDelivery(hqRoot: string, targetKey: string, now?: () => Date, kind?: MonitorTargetActivity["last_delivery_kind"]): Promise<void>;
|
|
147
|
+
export declare function recordMonitorTargetActivity(hqRoot: string, targetKey: string, now?: () => Date): Promise<void>;
|
|
148
|
+
export declare function recordMonitorTargetDeliveryAtPath(activityPath: string, now?: () => Date, kind?: MonitorTargetActivity["last_delivery_kind"]): void;
|
|
149
|
+
/** Elapsed idle time in whole units; live timers never land on exact multiples. */
|
|
150
|
+
export declare function formatElapsed(ms: number): string;
|
|
151
|
+
export declare function runMonitorCheckinTick(hqRoot: string, targetKey: string, deps: MonitorCheckinDeps): Promise<MonitorCheckinResult>;
|
|
152
|
+
export declare function readMonitorPidLiveness(pid: number | null, starttime: number | null, readStarttime?: (pid: number) => number | null): boolean;
|
|
153
|
+
//# sourceMappingURL=monitor.d.ts.map
|