@indigoai-us/hq-cli 5.158.0 → 5.160.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 +33 -0
- package/dist/command-catalog.generated.d.ts +29 -3
- package/dist/command-catalog.generated.js +36 -3
- package/dist/commands/agents.d.ts +5 -0
- package/dist/commands/agents.js +114 -3
- package/dist/commands/lanes.js +128 -11
- package/dist/lib/core-utils/sentry-report.d.ts +1 -1
- package/dist/lib/core-utils/sentry-report.js +107 -4
- package/dist/lib/lanes/errors.d.ts +1 -1
- package/dist/lib/lanes/profile.d.ts +5 -5
- package/dist/lib/lanes/profile.js +50 -13
- package/dist/lib/lanes/reconcile.js +20 -9
- package/dist/lib/lanes/spawn.d.ts +12 -1
- package/dist/lib/lanes/spawn.js +79 -16
- package/dist/lib/lanes/store.js +5 -0
- package/dist/lib/lanes/types.d.ts +5 -3
- package/dist/lib/lanes/types.js +2 -2
- package/dist/lib/lanes/watcher.d.ts +14 -8
- package/dist/lib/lanes/watcher.js +67 -25
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.160.0] — 2026-09-24
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Worker profiles can deliver validated policy files through an installed HQ
|
|
10
|
+
hook or include them in the prompt when the hook is unavailable. Resume and
|
|
11
|
+
message can attach a profile, and resume can attach a mesh company. Bot-owned
|
|
12
|
+
Claude profiles may rely on provider configuration for optional model,
|
|
13
|
+
account, effort, and fast-mode values.
|
|
14
|
+
|
|
15
|
+
- Added `hq agents login-code`, which submits a Claude subscription code from
|
|
16
|
+
an argument or stdin. Provisioning help shows the Agents v2 command and
|
|
17
|
+
account requirement. Claude subscription creates print the console URL and
|
|
18
|
+
login-code command.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- If a Codex or Claude worker exits without writing this run's envelope, hq lanes
|
|
23
|
+
gives its session one resume turn to write the expected file. The watcher and
|
|
24
|
+
dead-host recovery ignore earlier envelopes. If the repair still writes no
|
|
25
|
+
valid envelope, the lane remains interrupted.
|
|
26
|
+
|
|
27
|
+
## [5.159.0] — 2026-09-24
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Hook timeout reports now identify the slow child hook and group concurrent
|
|
32
|
+
session-wide slowness together. Update checks have bounded network calls, and
|
|
33
|
+
the core-write guard avoids repeated subprocess work.
|
|
34
|
+
- Machine-wide hook warnings and exceeded timeouts now keep separate Sentry
|
|
35
|
+
groups, and per-script rate limits no longer suppress a later machine-wide
|
|
36
|
+
report from the same session.
|
|
37
|
+
|
|
5
38
|
## [5.158.0] — 2026-09-24
|
|
6
39
|
|
|
7
40
|
### Added
|
|
@@ -3933,10 +3933,10 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
3933
3933
|
readonly description: "Agent slug (defaults to a slug of <name>)";
|
|
3934
3934
|
}, {
|
|
3935
3935
|
readonly flags: "--provider <provider>";
|
|
3936
|
-
readonly description: "Runtime: agents-v2 (recommended) | codex | grok | claude (
|
|
3936
|
+
readonly description: "Runtime: agents-v2 (recommended) | codex | grok | claude (accepted for script compatibility; use agents-v2 for new Claude agents)";
|
|
3937
3937
|
}, {
|
|
3938
3938
|
readonly flags: "--model <model>";
|
|
3939
|
-
readonly description: "Brain model, for example gpt-5.5 or grok-4.6";
|
|
3939
|
+
readonly description: "Brain model, for example claude-opus-5-5, gpt-5.5, or grok-4.6";
|
|
3940
3940
|
}, {
|
|
3941
3941
|
readonly flags: "--auth-mode <mode>";
|
|
3942
3942
|
readonly description: "Auth: subscription | apiKey (default subscription)";
|
|
@@ -3969,6 +3969,23 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
3969
3969
|
readonly description: "Emit machine-readable output and never open a browser";
|
|
3970
3970
|
}];
|
|
3971
3971
|
readonly subcommands: readonly [];
|
|
3972
|
+
}, {
|
|
3973
|
+
readonly name: "login-code";
|
|
3974
|
+
readonly description: "Submit a Claude subscription sign-in code for an agent";
|
|
3975
|
+
readonly aliases: readonly [];
|
|
3976
|
+
readonly hidden: false;
|
|
3977
|
+
readonly usage: "[options] <agentUid> [code]";
|
|
3978
|
+
readonly arguments: readonly [{
|
|
3979
|
+
readonly name: "agentUid";
|
|
3980
|
+
readonly required: true;
|
|
3981
|
+
readonly variadic: false;
|
|
3982
|
+
}, {
|
|
3983
|
+
readonly name: "code";
|
|
3984
|
+
readonly required: false;
|
|
3985
|
+
readonly variadic: false;
|
|
3986
|
+
}];
|
|
3987
|
+
readonly options: readonly [];
|
|
3988
|
+
readonly subcommands: readonly [];
|
|
3972
3989
|
}, {
|
|
3973
3990
|
readonly name: "list";
|
|
3974
3991
|
readonly description: "List the company's agents";
|
|
@@ -6784,7 +6801,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
6784
6801
|
readonly description: "Path to the lane brief";
|
|
6785
6802
|
}, {
|
|
6786
6803
|
readonly flags: "--worker <name>";
|
|
6787
|
-
readonly description: "HQ worker
|
|
6804
|
+
readonly description: "HQ worker or reusable profile id";
|
|
6788
6805
|
}, {
|
|
6789
6806
|
readonly flags: "--provider <name>";
|
|
6790
6807
|
readonly description: "claude | codex | grok";
|
|
@@ -7166,6 +7183,9 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
7166
7183
|
}, {
|
|
7167
7184
|
readonly flags: "--model <id>";
|
|
7168
7185
|
readonly description: "Model for the moved lane (with --account only)";
|
|
7186
|
+
}, {
|
|
7187
|
+
readonly flags: "--worker <profile>";
|
|
7188
|
+
readonly description: "Attach a personal worker profile when this message resumes the lane";
|
|
7169
7189
|
}, {
|
|
7170
7190
|
readonly flags: "--no-compact";
|
|
7171
7191
|
readonly description: "Skip the resume-time compaction check for this message";
|
|
@@ -7200,6 +7220,12 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
7200
7220
|
}, {
|
|
7201
7221
|
readonly flags: "--model <id>";
|
|
7202
7222
|
readonly description: "Model for the moved lane (with --account only)";
|
|
7223
|
+
}, {
|
|
7224
|
+
readonly flags: "--worker <profile>";
|
|
7225
|
+
readonly description: "Attach a personal worker profile for the resumed turn";
|
|
7226
|
+
}, {
|
|
7227
|
+
readonly flags: "--mesh-company <slug>";
|
|
7228
|
+
readonly description: "Attach Work Mesh attribution after verifying company membership";
|
|
7203
7229
|
}, {
|
|
7204
7230
|
readonly flags: "--no-compact";
|
|
7205
7231
|
readonly description: "Skip the resume-time compaction check for this resume";
|
|
@@ -5084,11 +5084,11 @@ export const COMMAND_CATALOG = [
|
|
|
5084
5084
|
},
|
|
5085
5085
|
{
|
|
5086
5086
|
"flags": "--provider <provider>",
|
|
5087
|
-
"description": "Runtime: agents-v2 (recommended) | codex | grok | claude (
|
|
5087
|
+
"description": "Runtime: agents-v2 (recommended) | codex | grok | claude (accepted for script compatibility; use agents-v2 for new Claude agents)"
|
|
5088
5088
|
},
|
|
5089
5089
|
{
|
|
5090
5090
|
"flags": "--model <model>",
|
|
5091
|
-
"description": "Brain model, for example gpt-5.5 or grok-4.6"
|
|
5091
|
+
"description": "Brain model, for example claude-opus-5-5, gpt-5.5, or grok-4.6"
|
|
5092
5092
|
},
|
|
5093
5093
|
{
|
|
5094
5094
|
"flags": "--auth-mode <mode>",
|
|
@@ -5133,6 +5133,27 @@ export const COMMAND_CATALOG = [
|
|
|
5133
5133
|
],
|
|
5134
5134
|
"subcommands": []
|
|
5135
5135
|
},
|
|
5136
|
+
{
|
|
5137
|
+
"name": "login-code",
|
|
5138
|
+
"description": "Submit a Claude subscription sign-in code for an agent",
|
|
5139
|
+
"aliases": [],
|
|
5140
|
+
"hidden": false,
|
|
5141
|
+
"usage": "[options] <agentUid> [code]",
|
|
5142
|
+
"arguments": [
|
|
5143
|
+
{
|
|
5144
|
+
"name": "agentUid",
|
|
5145
|
+
"required": true,
|
|
5146
|
+
"variadic": false
|
|
5147
|
+
},
|
|
5148
|
+
{
|
|
5149
|
+
"name": "code",
|
|
5150
|
+
"required": false,
|
|
5151
|
+
"variadic": false
|
|
5152
|
+
}
|
|
5153
|
+
],
|
|
5154
|
+
"options": [],
|
|
5155
|
+
"subcommands": []
|
|
5156
|
+
},
|
|
5136
5157
|
{
|
|
5137
5158
|
"name": "list",
|
|
5138
5159
|
"description": "List the company's agents",
|
|
@@ -8767,7 +8788,7 @@ export const COMMAND_CATALOG = [
|
|
|
8767
8788
|
},
|
|
8768
8789
|
{
|
|
8769
8790
|
"flags": "--worker <name>",
|
|
8770
|
-
"description": "HQ worker
|
|
8791
|
+
"description": "HQ worker or reusable profile id"
|
|
8771
8792
|
},
|
|
8772
8793
|
{
|
|
8773
8794
|
"flags": "--provider <name>",
|
|
@@ -9265,6 +9286,10 @@ export const COMMAND_CATALOG = [
|
|
|
9265
9286
|
"flags": "--model <id>",
|
|
9266
9287
|
"description": "Model for the moved lane (with --account only)"
|
|
9267
9288
|
},
|
|
9289
|
+
{
|
|
9290
|
+
"flags": "--worker <profile>",
|
|
9291
|
+
"description": "Attach a personal worker profile when this message resumes the lane"
|
|
9292
|
+
},
|
|
9268
9293
|
{
|
|
9269
9294
|
"flags": "--no-compact",
|
|
9270
9295
|
"description": "Skip the resume-time compaction check for this message"
|
|
@@ -9310,6 +9335,14 @@ export const COMMAND_CATALOG = [
|
|
|
9310
9335
|
"flags": "--model <id>",
|
|
9311
9336
|
"description": "Model for the moved lane (with --account only)"
|
|
9312
9337
|
},
|
|
9338
|
+
{
|
|
9339
|
+
"flags": "--worker <profile>",
|
|
9340
|
+
"description": "Attach a personal worker profile for the resumed turn"
|
|
9341
|
+
},
|
|
9342
|
+
{
|
|
9343
|
+
"flags": "--mesh-company <slug>",
|
|
9344
|
+
"description": "Attach Work Mesh attribution after verifying company membership"
|
|
9345
|
+
},
|
|
9313
9346
|
{
|
|
9314
9347
|
"flags": "--no-compact",
|
|
9315
9348
|
"description": "Skip the resume-time compaction check for this resume"
|
|
@@ -275,6 +275,11 @@ export declare function startStopAgent(token: string, agentUid: string, action:
|
|
|
275
275
|
uid: string;
|
|
276
276
|
runtime?: Record<string, unknown>;
|
|
277
277
|
}>;
|
|
278
|
+
/** Submit a human-provided Claude subscription code through hq-pro's route. */
|
|
279
|
+
export declare function submitAgentLoginCode(token: string, agentUid: string, code: string): Promise<{
|
|
280
|
+
uid: string;
|
|
281
|
+
ok: boolean;
|
|
282
|
+
}>;
|
|
278
283
|
/** `POST /v1/agents/{uid}/terminal` wire shape: vended session or 202 starting. */
|
|
279
284
|
export type AgentTerminalResponse = ({
|
|
280
285
|
ok: true;
|
package/dist/commands/agents.js
CHANGED
|
@@ -31,8 +31,10 @@ import open from "open";
|
|
|
31
31
|
import { randomUUID } from "node:crypto";
|
|
32
32
|
import * as readline from "node:readline";
|
|
33
33
|
import { resolveVaultCredential } from "../utils/resolve-vault-credential.js";
|
|
34
|
+
import { DEFAULT_VAULT_API_URL } from "../utils/cognito-session.js";
|
|
34
35
|
import { gateApiKeyCapabilities } from "../utils/api-key-command-gate.js";
|
|
35
36
|
import { vaultApiFetch, getCompanyUid } from "../utils/vault-api.js";
|
|
37
|
+
import { consoleOrigin } from "./integrations-connect.js";
|
|
36
38
|
import { launchSessionManagerPlugin, SessionManagerPluginLaunchError, TerminalSessionTimeoutError, waitForTerminalSession, } from "@indigoai-us/hq-cloud/outposts/node";
|
|
37
39
|
import { peekIdToken } from "../utils/id-token.js";
|
|
38
40
|
import { isPlanGateError } from "../utils/plan-gate-error.js";
|
|
@@ -54,6 +56,16 @@ export const VALID_PROVIDERS = new Set(["codex", "grok", "claude", "agents-v2"])
|
|
|
54
56
|
export const VALID_AUTH_MODES = new Set(["subscription", "apiKey"]);
|
|
55
57
|
/** Customer-facing agent size keys served by hq-pro's authoritative catalog. */
|
|
56
58
|
export const VALID_AGENT_SIZE_KEYS = new Set(["basic", "power", "dev"]);
|
|
59
|
+
const CLAUDE_PROVISION_COMMAND = "hq agents provision <name> --provider agents-v2 --model claude-opus-5-5";
|
|
60
|
+
const CLAUDE_API_KEY_ARGS = "--auth-mode apiKey --api-key-env <VAR>";
|
|
61
|
+
function claudeProvisionGuidance() {
|
|
62
|
+
return [
|
|
63
|
+
"The account must be enabled for Claude. Subscription auth is the default.",
|
|
64
|
+
`Create an agent with: ${CLAUDE_PROVISION_COMMAND}`,
|
|
65
|
+
`For API-key auth, add: ${CLAUDE_API_KEY_ARGS}`,
|
|
66
|
+
"The CLI accepts --provider claude for script compatibility, but the server rejects new agents that use it.",
|
|
67
|
+
].join("\n");
|
|
68
|
+
}
|
|
57
69
|
/**
|
|
58
70
|
* `hq agents create` exits with this distinct status when payment is required.
|
|
59
71
|
* Scripts can distinguish the expected upgrade state from an ordinary command
|
|
@@ -693,6 +705,54 @@ export async function startStopAgent(token, agentUid, action) {
|
|
|
693
705
|
method: "POST",
|
|
694
706
|
});
|
|
695
707
|
}
|
|
708
|
+
/** Submit a human-provided Claude subscription code through hq-pro's route. */
|
|
709
|
+
export async function submitAgentLoginCode(token, agentUid, code) {
|
|
710
|
+
return agentsRequest({
|
|
711
|
+
token,
|
|
712
|
+
path: `/v1/agents/${encodeURIComponent(agentUid)}/login-code`,
|
|
713
|
+
method: "POST",
|
|
714
|
+
body: { code },
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
async function readLoginCode(code) {
|
|
718
|
+
let value = code;
|
|
719
|
+
if (value === undefined) {
|
|
720
|
+
if (process.stdin.isTTY) {
|
|
721
|
+
console.error("Enter the Claude sign-in code, then press Enter:");
|
|
722
|
+
}
|
|
723
|
+
const rl = readline.createInterface({ input: process.stdin });
|
|
724
|
+
try {
|
|
725
|
+
value = await new Promise((resolve, reject) => {
|
|
726
|
+
rl.once("error", reject);
|
|
727
|
+
rl.once("close", () => resolve(""));
|
|
728
|
+
rl.question("", resolve);
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
finally {
|
|
732
|
+
rl.close();
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
const trimmed = value.trim();
|
|
736
|
+
if (!trimmed) {
|
|
737
|
+
throw new Error("Claude sign-in code required. Pass it as an argument or provide it on stdin.");
|
|
738
|
+
}
|
|
739
|
+
return trimmed;
|
|
740
|
+
}
|
|
741
|
+
function redactLoginCode(message, code) {
|
|
742
|
+
return code ? message.split(code).join("[redacted]") : message;
|
|
743
|
+
}
|
|
744
|
+
function agentConsoleResolveUrl(agentUid) {
|
|
745
|
+
const apiUrl = process.env.HQ_VAULT_API_URL?.trim() || DEFAULT_VAULT_API_URL;
|
|
746
|
+
const origin = consoleOrigin(apiUrl);
|
|
747
|
+
if (!origin)
|
|
748
|
+
return undefined;
|
|
749
|
+
// Keep the owner's canonical production sign-in URL while honoring an
|
|
750
|
+
// explicit console override or deriving the console for another API host.
|
|
751
|
+
const consoleBase = !process.env.HQ_CONSOLE_URL?.trim() && origin === "https://hq.computer"
|
|
752
|
+
? "https://hq.getindigo.ai"
|
|
753
|
+
: origin;
|
|
754
|
+
return `${consoleBase}/resolve/agents/${encodeURIComponent(agentUid)}`;
|
|
755
|
+
}
|
|
696
756
|
export async function openAgentTerminal(token, agentUid, body) {
|
|
697
757
|
return agentsRequest({
|
|
698
758
|
token,
|
|
@@ -1213,8 +1273,8 @@ export function registerAgentsCommand(program) {
|
|
|
1213
1273
|
.description("Provision a new cloud agent (company-specific monthly price shown before creation)")
|
|
1214
1274
|
.option("--company <slug>", "Company slug (resolves to companyUid)")
|
|
1215
1275
|
.option("--slug <slug>", "Agent slug (defaults to a slug of <name>)")
|
|
1216
|
-
.option("--provider <provider>", "Runtime: agents-v2 (recommended) | codex | grok | claude (
|
|
1217
|
-
.option("--model <model>", "Brain model, for example gpt-5.5 or grok-4.6")
|
|
1276
|
+
.option("--provider <provider>", "Runtime: agents-v2 (recommended) | codex | grok | claude (accepted for script compatibility; use agents-v2 for new Claude agents)")
|
|
1277
|
+
.option("--model <model>", "Brain model, for example claude-opus-5-5, gpt-5.5, or grok-4.6")
|
|
1218
1278
|
.option("--auth-mode <mode>", "Auth: subscription | apiKey (default subscription)", "subscription")
|
|
1219
1279
|
.option("--api-key-env <VAR>", "Env var holding the API key for --auth-mode apiKey (never pass the key as a flag)")
|
|
1220
1280
|
.option("--slack-bot-token <xoxb-token>", "Reuse an existing Slack bot token; HQ_SLACK_BOT_TOKEN or --slack-tokens-stdin avoid shell history")
|
|
@@ -1225,6 +1285,7 @@ export function registerAgentsCommand(program) {
|
|
|
1225
1285
|
.option("--size <size>", "Agent box size: basic | power | dev (omitted keeps the current default)")
|
|
1226
1286
|
.option("--yes", "Confirm the quoted monthly cost and provision the agent")
|
|
1227
1287
|
.option("--json", "Emit machine-readable output and never open a browser")
|
|
1288
|
+
.addHelpText("after", `\n${claudeProvisionGuidance()}\n`)
|
|
1228
1289
|
.action(async function (name, opts) {
|
|
1229
1290
|
try {
|
|
1230
1291
|
// Both of these previously fell back to their default on an
|
|
@@ -1251,7 +1312,7 @@ export function registerAgentsCommand(program) {
|
|
|
1251
1312
|
// AFTER clearing the charge gate. The server stays authoritative; this is
|
|
1252
1313
|
// only a friendlier, earlier copy of the same rule.
|
|
1253
1314
|
if (provider === "claude" && authMode === "apiKey") {
|
|
1254
|
-
console.error(chalk.red(
|
|
1315
|
+
console.error(chalk.red(`Use agents-v2 for new Claude agents. The claude provider remains accepted for script compatibility.\n${claudeProvisionGuidance()}`));
|
|
1255
1316
|
process.exit(1);
|
|
1256
1317
|
}
|
|
1257
1318
|
// Resolve the API key from the environment (never from a flag value) when
|
|
@@ -1317,6 +1378,15 @@ export function registerAgentsCommand(program) {
|
|
|
1317
1378
|
surface: CLI_AGENT_CREATE_SURFACE,
|
|
1318
1379
|
});
|
|
1319
1380
|
const identity = provisionedAgentIdentity(result);
|
|
1381
|
+
const claudeSignIn = provider === "agents-v2" &&
|
|
1382
|
+
authMode === "subscription" &&
|
|
1383
|
+
opts.model?.toLowerCase().startsWith("claude-") &&
|
|
1384
|
+
identity
|
|
1385
|
+
? {
|
|
1386
|
+
consoleUrl: agentConsoleResolveUrl(identity.uid) ?? null,
|
|
1387
|
+
loginCodeCommand: `hq agents login-code ${identity.uid}`,
|
|
1388
|
+
}
|
|
1389
|
+
: undefined;
|
|
1320
1390
|
const status = identity
|
|
1321
1391
|
? await getAgentStatus(token, identity.uid)
|
|
1322
1392
|
: null;
|
|
@@ -1336,10 +1406,20 @@ export function registerAgentsCommand(program) {
|
|
|
1336
1406
|
slackInstallUrl: slackInstall.url,
|
|
1337
1407
|
}
|
|
1338
1408
|
: {}),
|
|
1409
|
+
...(claudeSignIn ? { claudeSignIn } : {}),
|
|
1339
1410
|
}));
|
|
1340
1411
|
}
|
|
1341
1412
|
else {
|
|
1342
1413
|
console.log(chalk.green(`Provisioning started for agent "${name}".`));
|
|
1414
|
+
if (claudeSignIn) {
|
|
1415
|
+
if (claudeSignIn.consoleUrl) {
|
|
1416
|
+
console.log(chalk.yellow(`To sign in, open ${claudeSignIn.consoleUrl}, authorize Claude, then paste the code on that page.`));
|
|
1417
|
+
}
|
|
1418
|
+
else {
|
|
1419
|
+
console.log(chalk.yellow("The console URL for this control plane is unknown. Set HQ_CONSOLE_URL to configure it."));
|
|
1420
|
+
}
|
|
1421
|
+
console.log(chalk.dim(`A human CLI session can also submit the code with: ${claudeSignIn.loginCodeCommand}`));
|
|
1422
|
+
}
|
|
1343
1423
|
if (actions.length > 0) {
|
|
1344
1424
|
printPendingOperatorActions(identity?.slug ?? null, actions);
|
|
1345
1425
|
}
|
|
@@ -1349,6 +1429,11 @@ export function registerAgentsCommand(program) {
|
|
|
1349
1429
|
}
|
|
1350
1430
|
}
|
|
1351
1431
|
catch (err) {
|
|
1432
|
+
if (provider === "claude" &&
|
|
1433
|
+
err instanceof AgentsHttpError &&
|
|
1434
|
+
err.code === "LEGACY_MANAGED_PROVIDER_RETIRED") {
|
|
1435
|
+
throw new Error(`${err.message}\n\n${claudeProvisionGuidance()}`);
|
|
1436
|
+
}
|
|
1352
1437
|
if (err instanceof AgentsHttpError &&
|
|
1353
1438
|
err.status === 403 &&
|
|
1354
1439
|
err.code === "AGENT_PLAN_LIMIT") {
|
|
@@ -1377,6 +1462,32 @@ export function registerAgentsCommand(program) {
|
|
|
1377
1462
|
fail(err);
|
|
1378
1463
|
}
|
|
1379
1464
|
});
|
|
1465
|
+
agents
|
|
1466
|
+
.command("login-code <agentUid> [code]")
|
|
1467
|
+
.description("Submit a Claude subscription sign-in code for an agent")
|
|
1468
|
+
.action(async (agentUid, codeArg) => {
|
|
1469
|
+
let code = "";
|
|
1470
|
+
try {
|
|
1471
|
+
code = await readLoginCode(codeArg);
|
|
1472
|
+
const token = (await resolveVaultCredential()).token;
|
|
1473
|
+
await submitAgentLoginCode(token, agentUid, code);
|
|
1474
|
+
console.log(chalk.green(`Sent the Claude sign-in code to agent ${agentUid}.`));
|
|
1475
|
+
}
|
|
1476
|
+
catch (err) {
|
|
1477
|
+
if (err instanceof AgentsHttpError &&
|
|
1478
|
+
err.status === 403 &&
|
|
1479
|
+
err.code === "OUTPOST_ACCOUNT_MANAGEMENT_FORBIDDEN") {
|
|
1480
|
+
const consoleUrl = agentConsoleResolveUrl(agentUid);
|
|
1481
|
+
console.error(chalk.yellow(consoleUrl
|
|
1482
|
+
? `A person must paste this code in the agent console: ${consoleUrl}`
|
|
1483
|
+
: "A person must paste this code in the agent console. Set HQ_CONSOLE_URL to configure it for this control plane."));
|
|
1484
|
+
process.exit(1);
|
|
1485
|
+
}
|
|
1486
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1487
|
+
console.error(chalk.red(`Could not submit Claude sign-in code: ${redactLoginCode(detail, code)}`));
|
|
1488
|
+
process.exit(1);
|
|
1489
|
+
}
|
|
1490
|
+
});
|
|
1380
1491
|
agents
|
|
1381
1492
|
.command("list")
|
|
1382
1493
|
.description("List the company's agents")
|
package/dist/commands/lanes.js
CHANGED
|
@@ -3312,6 +3312,77 @@ async function runAdmit(ctx, laneId, opts) {
|
|
|
3312
3312
|
});
|
|
3313
3313
|
}, ctx.sleep);
|
|
3314
3314
|
}
|
|
3315
|
+
function resolveResumeProfile(ctx, lane, workerId) {
|
|
3316
|
+
const worker = resolveLaneWorker(ctx.hqRoot(), workerId.trim(), lane.company?.slug ?? lane.mesh_company?.slug);
|
|
3317
|
+
if (worker.profile === undefined) {
|
|
3318
|
+
throw new LanesError("worker_profile_required", `Worker ${JSON.stringify(workerId)} resolves to a registry worker, not a reusable provider profile; resume --worker requires personal/workers/<id>/meta.yaml`, { worker: workerId, path: worker.path });
|
|
3319
|
+
}
|
|
3320
|
+
if (worker.profile.meta.provider !== lane.provider) {
|
|
3321
|
+
throw new LanesError("worker_profile_provider_mismatch", `Worker profile ${JSON.stringify(workerId)} uses provider ${worker.profile.meta.provider}, but lane ${lane.lane_id} uses ${lane.provider}; resume cannot change a lane's provider`, {
|
|
3322
|
+
worker: workerId,
|
|
3323
|
+
lane_id: lane.lane_id,
|
|
3324
|
+
lane_provider: lane.provider,
|
|
3325
|
+
profile_provider: worker.profile.meta.provider,
|
|
3326
|
+
});
|
|
3327
|
+
}
|
|
3328
|
+
return worker;
|
|
3329
|
+
}
|
|
3330
|
+
function validateResumeProfileModel(ctx, lane, profile) {
|
|
3331
|
+
if (profile.meta.provider !== lane.provider ||
|
|
3332
|
+
profile.meta.model === undefined ||
|
|
3333
|
+
lane.model_source === "cli") {
|
|
3334
|
+
return;
|
|
3335
|
+
}
|
|
3336
|
+
const hqRoot = ctx.hqRoot();
|
|
3337
|
+
const config = loadLanesConfig(hqRoot);
|
|
3338
|
+
const account = resolveAccount(lane.provider, lane.account, config, {
|
|
3339
|
+
hqRoot,
|
|
3340
|
+
existsSync: ctx.existsSync,
|
|
3341
|
+
model: profile.meta.model,
|
|
3342
|
+
});
|
|
3343
|
+
validateProfileLaneModel(lane.provider, profile.meta.model, account.home, profile.id, {
|
|
3344
|
+
readFileSync: ctx.readFileSync,
|
|
3345
|
+
probe: ctx.probeProvider,
|
|
3346
|
+
env: ctx.env,
|
|
3347
|
+
});
|
|
3348
|
+
}
|
|
3349
|
+
function recordResumeSetup(ctx, lane, input) {
|
|
3350
|
+
if (input.profile === undefined && input.meshCompany === undefined)
|
|
3351
|
+
return lane;
|
|
3352
|
+
return casUpdateLane(ctx.hqRoot(), lane.lane_id, (current) => {
|
|
3353
|
+
if (current.version !== lane.version) {
|
|
3354
|
+
throw new LanesError("lane_resume_setup_conflict", `Lane ${lane.lane_id} changed while resume settings were prepared; run the resume again`, { lane_id: lane.lane_id, prepared_version: lane.version, version: current.version });
|
|
3355
|
+
}
|
|
3356
|
+
const next = { ...current };
|
|
3357
|
+
if (input.profile !== undefined) {
|
|
3358
|
+
const profile = input.profile.profile;
|
|
3359
|
+
next.worker = input.profile.id;
|
|
3360
|
+
next.worker_path = input.profile.path;
|
|
3361
|
+
next.worker_pack = input.profile.pack;
|
|
3362
|
+
next.worker_profile = profile.id;
|
|
3363
|
+
next.worker_profile_hash = profile.contentHash;
|
|
3364
|
+
next.worker_profile_ignored = ignoredWorkerProfileFields(current.provider);
|
|
3365
|
+
next.worker_policy_delivery = undefined;
|
|
3366
|
+
if (current.model_source !== "cli") {
|
|
3367
|
+
if (profile.meta.provider === current.provider && profile.meta.model !== undefined) {
|
|
3368
|
+
next.model = profile.meta.model;
|
|
3369
|
+
next.model_source = "profile";
|
|
3370
|
+
}
|
|
3371
|
+
else if (profile.meta.provider === current.provider) {
|
|
3372
|
+
next.model = undefined;
|
|
3373
|
+
next.model_source = "none";
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
if (input.meshCompany !== undefined) {
|
|
3378
|
+
if (current.company === null)
|
|
3379
|
+
next.mesh_company = input.meshCompany;
|
|
3380
|
+
if (input.threadId !== undefined)
|
|
3381
|
+
next.thread_id = input.threadId;
|
|
3382
|
+
}
|
|
3383
|
+
return next;
|
|
3384
|
+
});
|
|
3385
|
+
}
|
|
3315
3386
|
/**
|
|
3316
3387
|
* US-023: move the lane's provider session to `--account` (same provider)
|
|
3317
3388
|
* before a resume or a message resume. Every check runs before anything is
|
|
@@ -3379,15 +3450,18 @@ async function prepareAccountMove(ctx, lane, input) {
|
|
|
3379
3450
|
}).logged_in !== true) {
|
|
3380
3451
|
throw new LanesError("account_not_logged_in", `Account ${target} for ${lane.provider} is not logged in (no credential file)`, { provider: lane.provider, account: target, lane_id: laneId });
|
|
3381
3452
|
}
|
|
3382
|
-
const profile = lane.worker_profile !== undefined
|
|
3453
|
+
const profile = input.workerProfile ?? (lane.worker_profile !== undefined
|
|
3383
3454
|
? loadWorkerProfile(hqRoot, lane.worker_profile)
|
|
3384
|
-
: undefined;
|
|
3455
|
+
: undefined);
|
|
3385
3456
|
const profileDefaultsApply = profile !== undefined && profile.meta.provider === lane.provider;
|
|
3386
|
-
const pinnedModel =
|
|
3387
|
-
? lane.model
|
|
3388
|
-
: undefined
|
|
3389
|
-
|
|
3390
|
-
|
|
3457
|
+
const pinnedModel = input.workerProfile !== undefined
|
|
3458
|
+
? lane.model_source === "cli" ? lane.model : undefined
|
|
3459
|
+
: lane.model_source === undefined || lane.model_source === "cli"
|
|
3460
|
+
? lane.model
|
|
3461
|
+
: undefined;
|
|
3462
|
+
const profileModel = profileDefaultsApply ? profile?.meta.model : undefined;
|
|
3463
|
+
if (input.cliModel === undefined && profileDefaultsApply && profileModel !== undefined && pinnedModel === undefined) {
|
|
3464
|
+
validateProfileLaneModel(lane.provider, profileModel, targetCfg.home, profile.id, {
|
|
3391
3465
|
readFileSync: ctx.readFileSync,
|
|
3392
3466
|
probe: ctx.probeProvider,
|
|
3393
3467
|
env: ctx.env,
|
|
@@ -3400,12 +3474,13 @@ async function prepareAccountMove(ctx, lane, input) {
|
|
|
3400
3474
|
// here, before anything is copied. A target that pins a different model
|
|
3401
3475
|
// refuses the lane's model unless the operator names one for the move.
|
|
3402
3476
|
const model = resolveLaneModel({
|
|
3403
|
-
cliModel: input.cliModel ??
|
|
3477
|
+
cliModel: input.cliModel ?? pinnedModel,
|
|
3478
|
+
profileModel,
|
|
3404
3479
|
accountModel: targetCfg.model,
|
|
3405
3480
|
providerModel: providerCfg.model,
|
|
3406
3481
|
});
|
|
3407
|
-
if (input.cliModel === undefined && lane.model !== undefined && targetCfg.model !== undefined &&
|
|
3408
|
-
throw new LanesError("model_unsupported", `Account ${target} pins model ${targetCfg.model}; lane ${laneId}
|
|
3482
|
+
if (input.cliModel === undefined && lane.model !== undefined && targetCfg.model !== undefined && model !== targetCfg.model) {
|
|
3483
|
+
throw new LanesError("model_unsupported", `Account ${target} pins model ${targetCfg.model}; lane ${laneId} resolves to ${model ?? lane.model}. Pass --model ${targetCfg.model} to move it onto that model`, { provider: lane.provider, account: target, model: model ?? lane.model, account_model: targetCfg.model, lane_id: laneId });
|
|
3409
3484
|
}
|
|
3410
3485
|
const block = quotaBlockFor(loadQuotaState(hqRoot), lane.provider, target, model, now);
|
|
3411
3486
|
if (block.blocked) {
|
|
@@ -3521,6 +3596,12 @@ async function runMessage(ctx, laneId, opts) {
|
|
|
3521
3596
|
message,
|
|
3522
3597
|
});
|
|
3523
3598
|
}
|
|
3599
|
+
if (opts.worker !== undefined && mechanism === "dropbox") {
|
|
3600
|
+
throw new LanesError("worker_profile_requires_resume", `Lane ${laneId} is still running; --worker can only be applied when message starts a resumed provider turn`, { lane_id: laneId, state: lane.state });
|
|
3601
|
+
}
|
|
3602
|
+
const resumeProfile = opts.worker !== undefined
|
|
3603
|
+
? resolveResumeProfile(ctx, lane, opts.worker)
|
|
3604
|
+
: undefined;
|
|
3524
3605
|
if (opts.model !== undefined && opts.account === undefined) {
|
|
3525
3606
|
throw new LanesError("model_requires_account", "--model on hq lanes message is only accepted together with --account", {});
|
|
3526
3607
|
}
|
|
@@ -3544,6 +3625,7 @@ async function runMessage(ctx, laneId, opts) {
|
|
|
3544
3625
|
move = await prepareAccountMove(ctx, lane, {
|
|
3545
3626
|
account: opts.account,
|
|
3546
3627
|
cliModel: opts.model?.trim() || undefined,
|
|
3628
|
+
...(resumeProfile !== undefined ? { workerProfile: resumeProfile.profile } : {}),
|
|
3547
3629
|
pending: { trigger: "message", text, message_id: messageId, compact: opts.compact !== false },
|
|
3548
3630
|
});
|
|
3549
3631
|
lane = move.lane;
|
|
@@ -3551,6 +3633,11 @@ async function runMessage(ctx, laneId, opts) {
|
|
|
3551
3633
|
archiveResumeMessage(hqRoot, laneId, dropboxMessage);
|
|
3552
3634
|
}
|
|
3553
3635
|
}
|
|
3636
|
+
if (resumeProfile !== undefined) {
|
|
3637
|
+
if (opts.account === undefined)
|
|
3638
|
+
validateResumeProfileModel(ctx, lane, resumeProfile.profile);
|
|
3639
|
+
lane = recordResumeSetup(ctx, lane, { profile: resumeProfile });
|
|
3640
|
+
}
|
|
3554
3641
|
if (move?.queued) {
|
|
3555
3642
|
// Parked at the target's cap: reconcile resumes the session there.
|
|
3556
3643
|
}
|
|
@@ -3790,11 +3877,32 @@ async function runResume(ctx, laneId, opts) {
|
|
|
3790
3877
|
const storyId = opts.story?.trim() || lane.story_id;
|
|
3791
3878
|
const attributionChanged = projectId !== lane.project_id || storyId !== lane.story_id;
|
|
3792
3879
|
const messageId = text !== undefined || attributionChanged ? mintMessageId() : undefined;
|
|
3880
|
+
let requestedMeshCompany;
|
|
3881
|
+
let meshThreadId;
|
|
3882
|
+
if (opts.meshCompany !== undefined) {
|
|
3883
|
+
const meshCompanySlug = opts.meshCompany.trim();
|
|
3884
|
+
if (!meshCompanySlug) {
|
|
3885
|
+
throw new LanesError("missing_mesh_company", "--mesh-company must be a non-empty company slug", {});
|
|
3886
|
+
}
|
|
3887
|
+
requestedMeshCompany = await ctx.resolveCompany(meshCompanySlug);
|
|
3888
|
+
if (lane.company !== null &&
|
|
3889
|
+
(lane.company.uid !== requestedMeshCompany.uid || lane.company.slug !== requestedMeshCompany.slug)) {
|
|
3890
|
+
throw new LanesError("mesh_company_mismatch", "--mesh-company must match the lane's company when the lane is company-owned", { company: lane.company.slug, mesh_company: requestedMeshCompany.slug });
|
|
3891
|
+
}
|
|
3892
|
+
meshThreadId = await ctx.mesh.ensureProjectThread(requestedMeshCompany, projectId);
|
|
3893
|
+
}
|
|
3894
|
+
const resumeProfile = opts.worker !== undefined
|
|
3895
|
+
? resolveResumeProfile(ctx, lane, opts.worker)
|
|
3896
|
+
: undefined;
|
|
3897
|
+
if (resumeProfile !== undefined && opts.account === undefined) {
|
|
3898
|
+
validateResumeProfileModel(ctx, lane, resumeProfile.profile);
|
|
3899
|
+
}
|
|
3793
3900
|
let move = null;
|
|
3794
3901
|
if (opts.account !== undefined) {
|
|
3795
3902
|
move = await prepareAccountMove(ctx, lane, {
|
|
3796
3903
|
account: opts.account,
|
|
3797
3904
|
cliModel: opts.model?.trim() || undefined,
|
|
3905
|
+
...(resumeProfile !== undefined ? { workerProfile: resumeProfile.profile } : {}),
|
|
3798
3906
|
pending: {
|
|
3799
3907
|
trigger: "resume",
|
|
3800
3908
|
resumed_from: resumedFrom,
|
|
@@ -3805,6 +3913,12 @@ async function runResume(ctx, laneId, opts) {
|
|
|
3805
3913
|
});
|
|
3806
3914
|
lane = move.lane;
|
|
3807
3915
|
}
|
|
3916
|
+
lane = recordResumeSetup(ctx, lane, {
|
|
3917
|
+
...(resumeProfile !== undefined ? { profile: resumeProfile } : {}),
|
|
3918
|
+
...(requestedMeshCompany !== undefined
|
|
3919
|
+
? { meshCompany: requestedMeshCompany, threadId: meshThreadId }
|
|
3920
|
+
: {}),
|
|
3921
|
+
});
|
|
3808
3922
|
let queuedOnAdmission = false;
|
|
3809
3923
|
let resumed;
|
|
3810
3924
|
if (move?.queued) {
|
|
@@ -5032,7 +5146,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
5032
5146
|
.requiredOption("--story <id>", "Story id from the project's prd.json")
|
|
5033
5147
|
.option("--additional-story <id>", "Additional story carried by this lane; repeatable and discouraged when active", collectRepeatable)
|
|
5034
5148
|
.requiredOption("--brief-file <path>", "Path to the lane brief")
|
|
5035
|
-
.option("--worker <name>", "HQ worker
|
|
5149
|
+
.option("--worker <name>", "HQ worker or reusable profile id")
|
|
5036
5150
|
.option("--provider <name>", "claude | codex | grok")
|
|
5037
5151
|
.option("--account <id>", "Provider account id")
|
|
5038
5152
|
.option("--model <id>", "Model id for the provider CLI")
|
|
@@ -5183,6 +5297,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
5183
5297
|
.option("--story <id>", "Re-point story attribution from this message")
|
|
5184
5298
|
.option("--account <id>", "Move the lane's session to this account of its provider before the message resumes it")
|
|
5185
5299
|
.option("--model <id>", "Model for the moved lane (with --account only)")
|
|
5300
|
+
.option("--worker <profile>", "Attach a personal worker profile when this message resumes the lane")
|
|
5186
5301
|
.option("--no-compact", "Skip the resume-time compaction check for this message")
|
|
5187
5302
|
.action(wrap(async (laneId, opts) => {
|
|
5188
5303
|
await runMessage(ctx, laneId, opts);
|
|
@@ -5196,6 +5311,8 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
5196
5311
|
.option("--story <id>", "Re-point story attribution from this resume")
|
|
5197
5312
|
.option("--account <id>", "Move the lane's session to this account of its provider before resuming")
|
|
5198
5313
|
.option("--model <id>", "Model for the moved lane (with --account only)")
|
|
5314
|
+
.option("--worker <profile>", "Attach a personal worker profile for the resumed turn")
|
|
5315
|
+
.option("--mesh-company <slug>", "Attach Work Mesh attribution after verifying company membership")
|
|
5199
5316
|
.option("--no-compact", "Skip the resume-time compaction check for this resume")
|
|
5200
5317
|
.action(wrap(async (laneId, opts) => {
|
|
5201
5318
|
await runResume(ctx, laneId, opts);
|
|
@@ -27,7 +27,7 @@ export declare const MAX_SENTRY_REPORT_METADATA_VALUE_BYTES = 256;
|
|
|
27
27
|
* is owner-approved telemetry for the exact hook script and may include a
|
|
28
28
|
* company directory; it is never a substitute for hook input or a transcript.
|
|
29
29
|
*/
|
|
30
|
-
export declare const ALLOWED_SENTRY_REPORT_METADATA_KEYS: readonly ["hook_name", "hook_path", "hook_event", "hook_script", "tool_name", "session_id", "declared_timeout_ms", "elapsed_ms", "remaining_ms", "watchdog_timeout_ms", "hook_timeout_ms", "hq_version", "platform", "load_average", "exit_code", "final_elapsed_ms", "bash_env_set", "cwd_kind", "shell", "nproc", "hook_sequence"];
|
|
30
|
+
export declare const ALLOWED_SENTRY_REPORT_METADATA_KEYS: readonly ["hook_name", "hook_path", "hook_event", "hook_script", "tool_name", "session_id", "declared_timeout_ms", "elapsed_ms", "remaining_ms", "watchdog_timeout_ms", "hook_timeout_ms", "hq_version", "platform", "load_average", "spawn_ms", "slow_child", "slow_child_ms", "exit_code", "final_elapsed_ms", "bash_env_set", "cwd_kind", "shell", "nproc", "hook_sequence"];
|
|
31
31
|
export declare const MAX_SENTRY_REPORT_METADATA_KEYS: number;
|
|
32
32
|
export type HookSequenceEntry = {
|
|
33
33
|
script: string;
|