@indigoai-us/hq-cli 5.140.0 → 5.142.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.
@@ -11,6 +11,7 @@ import { parseUserYaml } from "../../utils/user-yaml-error.js";
11
11
  import { accountMaxRunning, loadLanesConfig, providerQuotaDefaultHoldMs } from "./config.js";
12
12
  import { MAX_LANE_DEPTH } from "./constants.js";
13
13
  import { LanesError, isLaneVersionConflict } from "./errors.js";
14
+ import { loadWorkerProfile } from "./profile.js";
14
15
  import { classifySessionLiveness, defaultLivenessDeps, loadLivenessIndex, readDaemonPresenceConnected, } from "./liveness.js";
15
16
  import { ANSWERED_QUESTIONS_FILE, armLaneWatcher, isPidRecordAlive, laneRunDir, readPidRecord, readSpawnManifest, spawnEnvelopeRepair, spawnLaneWorker, } from "./spawn.js";
16
17
  import { appendCappedHistory, lanesStoreRoot, parseLaneRecord, parseQuestionRecord, readLane, readQuestion, updateLane, updateQuestion, } from "./store.js";
@@ -18,7 +19,7 @@ import { applyThrottleDecision, defaultThrottleDeps, emptyThrottleState, evaluat
18
19
  import { applyDefaultQuotaHold, readQuotaSignal, recordAccountQuota } from "./quota.js";
19
20
  import { answeredQuestionEntries, raiseEnvelopeQuestions } from "./questions.js";
20
21
  import { SILENCE_MS, classifyLaneExit, isPidAlive, readLatestEnvelope, readLatestEnvelopeFile, readLatestEnvelopeRecord, } from "./watcher.js";
21
- import { createLaneMesh, emitLaneDecisionEvents } from "./mesh-events.js";
22
+ import { createLaneMesh, emitLaneDecisionEvents, laneMeshCompany } from "./mesh-events.js";
22
23
  import { decideEnvelopeRepair, envelopeQuestionIds, routeProviderFailure } from "./watcher.js";
23
24
  import { providerRejectionDetails, readProviderRejection, } from "./provider-rejection.js";
24
25
  /** Spec §9: unanswered at the current hop for 60 minutes → one hop. */
@@ -317,13 +318,14 @@ async function emitAgedQuestionAtUser(summary, deps, lane, question) {
317
318
  if (deps.dryRun)
318
319
  return;
319
320
  const mesh = deps.mesh === undefined ? createLaneMesh({ interactive: false }) : deps.mesh;
320
- if (!mesh || !lane.company || !lane.thread_id)
321
+ const company = laneMeshCompany(lane);
322
+ if (!mesh || !company || !lane.thread_id)
321
323
  return;
322
324
  const timeoutMs = deps.meshEmitTimeoutMs ?? 10_000;
323
325
  let timer;
324
326
  try {
325
327
  await Promise.race([
326
- mesh.appendThreadEvent(lane.company, lane.thread_id, "lane_question_at_user", { lane_id: lane.lane_id, question_id: question.question_id }),
328
+ mesh.appendThreadEvent(company, lane.thread_id, "lane_question_at_user", { lane_id: lane.lane_id, question_id: question.question_id }),
327
329
  new Promise((_resolve, reject) => {
328
330
  timer = setTimeout(() => reject(new Error(`question-at-user mesh emit timed out after ${timeoutMs}ms`)), timeoutMs);
329
331
  }),
@@ -1011,6 +1013,26 @@ async function admitQueued(hqRoot, lanes, summary, deps, now) {
1011
1013
  continue;
1012
1014
  }
1013
1015
  }
1016
+ let profileMaxConcurrent;
1017
+ if (lane.worker_profile !== undefined) {
1018
+ try {
1019
+ profileMaxConcurrent = loadWorkerProfile(hqRoot, lane.worker_profile).meta.max_concurrent;
1020
+ }
1021
+ catch (err) {
1022
+ markDirty(summary, {
1023
+ kind: "lane",
1024
+ id: lane.lane_id,
1025
+ why: `profile admission failed: ${err instanceof Error ? err.message : String(err)}`,
1026
+ });
1027
+ continue;
1028
+ }
1029
+ }
1030
+ if (profileMaxConcurrent !== undefined) {
1031
+ const profileRunning = running.filter((row) => row.worker_profile === lane.worker_profile).length +
1032
+ spawned.filter((row) => row.worker_profile === lane.worker_profile).length;
1033
+ if (profileRunning >= profileMaxConcurrent)
1034
+ continue;
1035
+ }
1014
1036
  try {
1015
1037
  if (deps.dryRun) {
1016
1038
  summary.admitted.push({
@@ -23,6 +23,7 @@
23
23
  * `worker_tenancy_unresolved` when that cannot be determined.
24
24
  */
25
25
  import * as fs from "node:fs";
26
+ import { type WorkerProfile } from "./profile.js";
26
27
  export declare const WORKER_REGISTRY_REL: string;
27
28
  export interface RegistryWorkerEntry {
28
29
  id: string;
@@ -87,6 +88,8 @@ export interface ResolvedLaneWorker {
87
88
  humanCheckpoints: string[];
88
89
  /** Set when the worker's realpath lives in a package. */
89
90
  pack?: string;
91
+ /** Set when this worker was resolved from personal/workers/<id>/meta.yaml. */
92
+ profile?: WorkerProfile;
90
93
  }
91
94
  export interface RegistryDeps {
92
95
  existsSync: (p: string) => boolean;
@@ -28,6 +28,7 @@ import { parseUserYaml } from "../../utils/user-yaml-error.js";
28
28
  import { readContributes } from "../scan-packages/index.js";
29
29
  import { contributionLinks, linkStatus, } from "../../utils/pack-contributions.js";
30
30
  import { WorkerResolutionError } from "./errors.js";
31
+ import { loadWorkerProfile, workerProfileMarker, } from "./profile.js";
31
32
  export const WORKER_REGISTRY_REL = path.join("core", "workers", "registry.yaml");
32
33
  function defaultRegistryDeps() {
33
34
  return {
@@ -247,6 +248,9 @@ function personalDirClaim(hqRoot, workerId, deps) {
247
248
  const dir = personalWorkerDir(hqRoot, workerId);
248
249
  let st;
249
250
  try {
251
+ const linkStat = fs.lstatSync(dir);
252
+ if (linkStat.isSymbolicLink())
253
+ return "none";
250
254
  st = deps.statSync(dir);
251
255
  }
252
256
  catch (err) {
@@ -369,7 +373,10 @@ function readRegistryOrEmpty(hqRoot, deps) {
369
373
  }
370
374
  export function resolveLaneWorker(hqRoot, workerId, laneCompany, deps = defaultRegistryDeps()) {
371
375
  const personalRel = path.posix.join("personal", "workers", workerId);
372
- const personalClaim = personalDirClaim(hqRoot, workerId, deps);
376
+ const profile = workerProfileMarker(hqRoot, workerId) === "present"
377
+ ? loadWorkerProfile(hqRoot, workerId)
378
+ : undefined;
379
+ const personalClaim = profile ? "profile" : personalDirClaim(hqRoot, workerId, deps);
373
380
  if (personalClaim === "missing_yaml") {
374
381
  throw new WorkerResolutionError("worker_not_found", `Worker ${JSON.stringify(workerId)} is at ${personalRel}/ but worker.yaml is missing`, { worker: workerId, path: `${personalRel}/`, missing: "worker.yaml" });
375
382
  }
@@ -390,6 +397,43 @@ export function resolveLaneWorker(hqRoot, workerId, laneCompany, deps = defaultR
390
397
  realpath: realpathOf(hqRoot, rel),
391
398
  });
392
399
  }
400
+ if (profile !== undefined && candidates.length > 0) {
401
+ throw new WorkerResolutionError("worker_profile_ambiguous", "Worker " +
402
+ JSON.stringify(workerId) +
403
+ " has both a personal profile and a registry worker; name one explicitly instead of falling through", {
404
+ worker: workerId,
405
+ profile_path: personalRel + "/",
406
+ registry_paths: candidates.map((item) => item.path),
407
+ });
408
+ }
409
+ if (profile !== undefined) {
410
+ const description = "Reusable worker profile " + profile.id;
411
+ return {
412
+ id: profile.id,
413
+ path: personalRel + "/",
414
+ type: "WorkerProfile",
415
+ status: "active",
416
+ description,
417
+ tenancy: { kind: "global" },
418
+ workerYaml: {
419
+ worker: {
420
+ id: profile.id,
421
+ name: profile.id,
422
+ description,
423
+ type: "WorkerProfile",
424
+ status: "active",
425
+ },
426
+ instructions: "Apply reusable worker profile " + profile.id + " at every provider spawn.",
427
+ },
428
+ workerDir: profile.dir,
429
+ resolvedContextBase: [],
430
+ skillFiles: [],
431
+ defaultTimeout: undefined,
432
+ approvalRequired: false,
433
+ humanCheckpoints: [],
434
+ profile,
435
+ };
436
+ }
393
437
  if (personalClaim === "yaml") {
394
438
  const rel = normalizeRel(personalRel);
395
439
  const real = realpathOf(hqRoot, rel);
@@ -11,7 +11,7 @@ import { PROVIDER_HOME_ENV } from "./accounts.js";
11
11
  import { type RunCompactionFn } from "./compaction.js";
12
12
  import { type LaunchTrigger } from "./delivery.js";
13
13
  import { type MachineIdentity } from "./machine-identity.js";
14
- import { type LaneProvider, type LaneRecord } from "./types.js";
14
+ import { type LaneCompanyRef, type LaneProvider, type LaneRecord } from "./types.js";
15
15
  export declare const LANES_RUNS_REL: string;
16
16
  /** Written by reconcile before a resume spawn; the worker prompt names this path. */
17
17
  export declare const ANSWERED_QUESTIONS_FILE = "answered-questions.json";
@@ -27,12 +27,18 @@ export declare const CODEX_FAST_MODE_FLAGS: readonly ["-c", "service_tier=\"fast
27
27
  export declare function parseCliModel(raw: string | undefined): string | undefined;
28
28
  export declare function resolveLaneModel(input: {
29
29
  cliModel?: string;
30
+ profileModel?: string;
30
31
  accountModel?: string;
31
32
  providerModel?: string;
32
33
  }): string | undefined;
33
34
  export declare function appendModelFlag(provider: LaneProvider, args: string[], model: string | undefined): string[];
34
35
  /** Add Codex fast-mode flags before the prompt/positional separator. */
35
36
  export declare function appendFastModeFlags(provider: LaneProvider, args: string[], enabled: boolean | undefined): string[];
37
+ export interface ProviderProfileLaunch {
38
+ systemPrompt: string;
39
+ settingsPath?: string;
40
+ ignoredFields?: readonly string[];
41
+ }
36
42
  export type DetachedStdio = ["ignore", number, number];
37
43
  export interface DetachedSpawnOptions {
38
44
  detached: true;
@@ -197,6 +203,8 @@ export declare function buildProviderCommand(provider: LaneProvider, input: {
197
203
  effort?: string;
198
204
  /** Enable Codex's fast service tier for this resolved model. */
199
205
  fastMode?: boolean;
206
+ /** Per-spawn worker profile instructions and materialized settings. */
207
+ profile?: ProviderProfileLaunch;
200
208
  /** Continue a recorded session. Only providers in PROVIDER_SESSION_RESUME accept this. */
201
209
  resume?: ProviderResumeInput;
202
210
  /**
@@ -209,7 +217,7 @@ export declare function buildProviderCommand(provider: LaneProvider, input: {
209
217
  export declare function workerEnv(base: NodeJS.ProcessEnv, resolved: {
210
218
  envVar: string;
211
219
  home: string;
212
- }, laneId: string, hqRoot: string, autoCompactTokens?: number): NodeJS.ProcessEnv;
220
+ }, laneId: string, hqRoot: string, autoCompactTokens?: number, meshCompany?: LaneCompanyRef): NodeJS.ProcessEnv;
213
221
  export declare function markSpawnFailure(hqRoot: string, laneId: string, reasonRaw: string, details: Record<string, unknown>): LaneRecord;
214
222
  /**
215
223
  * Spec §5.2 executing-side check. Compares this machine's identity to
@@ -227,6 +235,10 @@ export interface SpawnManifestFile {
227
235
  /** Byte length of logs/worker.out before this run started; its result is after this. */
228
236
  worker_out_offset?: number;
229
237
  fast_mode?: boolean;
238
+ worker_profile?: string;
239
+ worker_profile_hash?: string;
240
+ worker_profile_ignored?: string[];
241
+ worker_profile_settings?: string;
230
242
  }
231
243
  /** Read the per-run manifest. Missing file or field reads as absent. */
232
244
  export declare function readSpawnManifest(runDir: string): SpawnManifestFile;
@@ -18,6 +18,7 @@ import { loadLanesConfig, providerAutoCompactTokens, providerCompactAfterMs, } f
18
18
  import { appendEffortFlag, resolveLaneEffort } from "./effort.js";
19
19
  import { claimableStatesFor, planLaneLaunch, providerSupportsSessionResume, } from "./delivery.js";
20
20
  import { AccountResolutionError, LanesError } from "./errors.js";
21
+ import { ignoredWorkerProfileFields, loadWorkerProfile, materializeWorkerProfile, workerProfilePrompt, } from "./profile.js";
21
22
  import { resolveMachineIdentity, } from "./machine-identity.js";
22
23
  import { readLane, retryUpdateLane } from "./store.js";
23
24
  import { LANE_ENVELOPE_JSON_SCHEMA, parseLaneProvider, } from "./types.js";
@@ -86,6 +87,9 @@ export function parseCliModel(raw) {
86
87
  export function resolveLaneModel(input) {
87
88
  if (input.cliModel !== undefined)
88
89
  return input.cliModel;
90
+ if (input.profileModel !== undefined && input.profileModel.trim().length > 0) {
91
+ return input.profileModel.trim();
92
+ }
89
93
  if (input.accountModel !== undefined && input.accountModel.trim().length > 0) {
90
94
  return input.accountModel.trim();
91
95
  }
@@ -119,6 +123,40 @@ export function appendFastModeFlags(provider, args, enabled) {
119
123
  return [...args, ...flags];
120
124
  return [...args.slice(0, end), ...flags, ...args.slice(end)];
121
125
  }
126
+ function appendProviderProfileFlags(provider, args, profile) {
127
+ if (profile === undefined)
128
+ return args;
129
+ const ignored = new Set(profile.ignoredFields ?? []);
130
+ switch (provider) {
131
+ case "claude": {
132
+ const flags = [
133
+ "--append-system-prompt",
134
+ profile.systemPrompt,
135
+ // A profile is a per-spawn input. Do not let Claude's session resume
136
+ // snapshot hide a changed profile from the next launch.
137
+ "--system-prompt-snapshot",
138
+ "off",
139
+ ...(profile.settingsPath !== undefined && !ignored.has("settings")
140
+ ? ["--settings", profile.settingsPath]
141
+ : []),
142
+ ];
143
+ return [...args, ...flags];
144
+ }
145
+ case "codex": {
146
+ const flags = ["-c", `developer_instructions=${JSON.stringify(profile.systemPrompt)}`];
147
+ const end = args.indexOf("--");
148
+ if (end === -1)
149
+ return [...args, ...flags];
150
+ return [...args.slice(0, end), ...flags, ...args.slice(end)];
151
+ }
152
+ case "grok":
153
+ return [...args, "--rules", profile.systemPrompt];
154
+ default: {
155
+ const _exhaustive = provider;
156
+ return _exhaustive;
157
+ }
158
+ }
159
+ }
122
160
  export const PID_LIVENESS_DECISIONS = [
123
161
  "esrch",
124
162
  "starttime_mismatch",
@@ -713,10 +751,10 @@ export function buildProviderCommand(provider, input) {
713
751
  }
714
752
  return {
715
753
  command,
716
- args: appendFastModeFlags(parsed, appendEffortFlag(parsed, appendModelFlag(parsed, args, input.model), input.effort), input.fastMode),
754
+ args: appendFastModeFlags(parsed, appendEffortFlag(parsed, appendModelFlag(parsed, appendProviderProfileFlags(parsed, args, input.profile), input.model), input.effort), input.fastMode),
717
755
  };
718
756
  }
719
- export function workerEnv(base, resolved, laneId, hqRoot, autoCompactTokens) {
757
+ export function workerEnv(base, resolved, laneId, hqRoot, autoCompactTokens, meshCompany) {
720
758
  const accepted = Object.values(PROVIDER_HOME_ENV);
721
759
  if (!accepted.includes(resolved.envVar)) {
722
760
  throw new LanesError("unknown_provider_home_env", `Unknown provider home variable ${JSON.stringify(resolved.envVar)}. Accepted: ${accepted.join(", ")}`, { accepted, value: resolved.envVar });
@@ -731,6 +769,12 @@ export function workerEnv(base, resolved, laneId, hqRoot, autoCompactTokens) {
731
769
  }
732
770
  env.HQ_LANE_ID = laneId;
733
771
  env.HQ_ROOT = hqRoot;
772
+ if (meshCompany) {
773
+ env.HQ_SPAWN_COMPANY = meshCompany.slug || meshCompany.uid;
774
+ }
775
+ else {
776
+ delete env.HQ_SPAWN_COMPANY;
777
+ }
734
778
  return env;
735
779
  }
736
780
  function ensureDir(dir) {
@@ -752,6 +796,15 @@ function writeText(filePath, text) {
752
796
  ensureDir(path.dirname(filePath));
753
797
  fs.writeFileSync(filePath, text, "utf8");
754
798
  }
799
+ function appendWorkerProfileEvent(runDir, event) {
800
+ const eventsPath = path.join(runDir, "watcher", "profile-events.jsonl");
801
+ ensureDir(path.dirname(eventsPath));
802
+ fs.appendFileSync(eventsPath, `${JSON.stringify({
803
+ event: "profile_applied",
804
+ ...event,
805
+ at: new Date().toISOString(),
806
+ })}\n`);
807
+ }
755
808
  function openLog(filePath) {
756
809
  ensureDir(path.dirname(filePath));
757
810
  return fs.openSync(filePath, "a");
@@ -906,6 +959,16 @@ function writeSpawnManifest(runDir, input) {
906
959
  resume: input.resume,
907
960
  worker_out_offset: input.workerOutOffset,
908
961
  ...(input.fastMode ? { fast_mode: true } : {}),
962
+ ...(input.profile
963
+ ? {
964
+ worker_profile: input.profile.id,
965
+ worker_profile_hash: input.profile.hash,
966
+ worker_profile_ignored: [...input.profile.ignoredFields],
967
+ ...(input.profile.settingsPath
968
+ ? { worker_profile_settings: input.profile.settingsPath }
969
+ : {}),
970
+ }
971
+ : {}),
909
972
  }, null, 2)}\n`);
910
973
  }
911
974
  /** Arm (or re-arm) the detached per-lane watcher. */
@@ -969,6 +1032,15 @@ export function readSpawnManifest(runDir) {
969
1032
  }
970
1033
  if (typeof rec.fast_mode === "boolean")
971
1034
  out.fast_mode = rec.fast_mode;
1035
+ if (typeof rec.worker_profile === "string")
1036
+ out.worker_profile = rec.worker_profile;
1037
+ if (typeof rec.worker_profile_hash === "string")
1038
+ out.worker_profile_hash = rec.worker_profile_hash;
1039
+ if (Array.isArray(rec.worker_profile_ignored) && rec.worker_profile_ignored.every((item) => typeof item === "string")) {
1040
+ out.worker_profile_ignored = rec.worker_profile_ignored;
1041
+ }
1042
+ if (typeof rec.worker_profile_settings === "string")
1043
+ out.worker_profile_settings = rec.worker_profile_settings;
972
1044
  if (rec.resume &&
973
1045
  typeof rec.resume === "object" &&
974
1046
  typeof rec.resume.session_id === "string") {
@@ -1024,13 +1096,38 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
1024
1096
  const logsDir = path.join(runDir, "logs");
1025
1097
  ensureDir(logsDir);
1026
1098
  ensureDir(path.join(runDir, "envelopes"));
1099
+ const profile = existing.worker_profile
1100
+ ? loadWorkerProfile(hqRoot, existing.worker_profile)
1101
+ : undefined;
1102
+ const profileDefaultsApply = profile !== undefined && profile.meta.provider === existing.provider;
1103
+ const profileModel = profileDefaultsApply ? profile.meta.model : undefined;
1104
+ const profileEffort = profileDefaultsApply ? profile.meta.effort : undefined;
1105
+ const ignoredProfileFields = profile
1106
+ ? ignoredWorkerProfileFields(existing.provider)
1107
+ : [];
1108
+ const profileRuntime = profile
1109
+ ? materializeWorkerProfile(profile, runDir)
1110
+ : undefined;
1111
+ const profileLaunch = profile
1112
+ ? {
1113
+ systemPrompt: workerProfilePrompt(profile),
1114
+ ...(profileRuntime?.settingsPath !== undefined
1115
+ ? { settingsPath: profileRuntime.settingsPath }
1116
+ : {}),
1117
+ ignoredFields: ignoredProfileFields,
1118
+ }
1119
+ : undefined;
1120
+ const profileChanged = profile !== undefined && existing.worker_profile_hash !== profile.contentHash;
1027
1121
  const config = loadLanesConfig(hqRoot);
1122
+ const pinnedModel = existing.model_source === undefined || existing.model_source === "cli"
1123
+ ? existing.model
1124
+ : undefined;
1028
1125
  let resolved;
1029
1126
  try {
1030
1127
  resolved = resolveAccount(lane.provider, lane.account, config, {
1031
1128
  hqRoot,
1032
1129
  existsSync,
1033
- model: lane.model,
1130
+ model: pinnedModel ?? profileModel,
1034
1131
  });
1035
1132
  }
1036
1133
  catch (err) {
@@ -1078,21 +1175,26 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
1078
1175
  const autoCompactTokens = lane.provider === "claude"
1079
1176
  ? providerAutoCompactTokens(config.providers.claude)
1080
1177
  : undefined;
1081
- const childEnv = workerEnv(env, { envVar: resolved.envVar, home: resolved.home }, lane.lane_id, hqRoot, autoCompactTokens);
1178
+ const childEnv = workerEnv(env, { envVar: resolved.envVar, home: resolved.home }, lane.lane_id, hqRoot, autoCompactTokens, lane.mesh_company ?? lane.company ?? undefined);
1082
1179
  const model = resolveLaneModel({
1083
- cliModel: lane.model,
1180
+ cliModel: pinnedModel,
1181
+ profileModel,
1084
1182
  accountModel: accountCfg?.model,
1085
1183
  providerModel: config.providers[lane.provider].model,
1086
1184
  });
1087
1185
  const effort = resolveLaneEffort({
1088
1186
  cliEffort: existing.effort_source === "cli" ? existing.effort : undefined,
1187
+ profileEffort,
1089
1188
  accountEffort: accountCfg?.effort,
1090
1189
  providerEffort: config.providers[lane.provider].effort,
1091
1190
  model,
1092
1191
  });
1093
- const fastMode = lane.provider === "codex" &&
1094
- model !== undefined &&
1095
- (config.providers.codex.fast_mode_models ?? []).includes(model);
1192
+ const fastMode = lane.provider === "codex"
1193
+ ? profileDefaultsApply
1194
+ ? profile?.meta.fast_mode === true
1195
+ : model !== undefined &&
1196
+ (config.providers.codex.fast_mode_models ?? []).includes(model)
1197
+ : false;
1096
1198
  const resume = plan.mode === "resume"
1097
1199
  ? {
1098
1200
  session_id: plan.session_id,
@@ -1122,6 +1224,7 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
1122
1224
  model,
1123
1225
  effort: effort.effort,
1124
1226
  fastMode,
1227
+ profile: profileLaunch,
1125
1228
  resume,
1126
1229
  });
1127
1230
  // A systemd PATH mismatch is an environment defect, not a lane failure.
@@ -1170,6 +1273,7 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
1170
1273
  model,
1171
1274
  effort: effort.effort,
1172
1275
  fastMode,
1276
+ profile: profileLaunch,
1173
1277
  resume: { session_id: resume.session_id, prompt: COMPACTION_PROMPT },
1174
1278
  });
1175
1279
  compaction = compactLaneSession({
@@ -1213,13 +1317,36 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
1213
1317
  pending_resume: undefined,
1214
1318
  account: resolved.id,
1215
1319
  ...(model !== undefined ? { model } : {}),
1320
+ ...(model !== undefined
1321
+ ? {
1322
+ model_source: pinnedModel !== undefined
1323
+ ? "cli"
1324
+ : profileDefaultsApply
1325
+ ? "profile"
1326
+ : accountCfg?.model !== undefined
1327
+ ? "account"
1328
+ : config.providers[lane.provider].model !== undefined
1329
+ ? "provider"
1330
+ : "none",
1331
+ }
1332
+ : { model_source: "none" }),
1216
1333
  effort_source: effort.source,
1217
1334
  last_host_report_at: nowIso,
1335
+ ...(profile
1336
+ ? {
1337
+ worker_profile_hash: profile.contentHash,
1338
+ worker_profile_ignored: [...ignoredProfileFields],
1339
+ }
1340
+ : {}),
1218
1341
  };
1219
1342
  if (effort.effort !== undefined)
1220
1343
  next.effort = effort.effort;
1221
1344
  else
1222
1345
  delete next.effort;
1346
+ // `undefined` deliberately overlays the CAS merge's current record so a
1347
+ // dynamic default disappearing removes the stale persisted model.
1348
+ if (model === undefined)
1349
+ next.model = undefined;
1223
1350
  if (fastMode)
1224
1351
  next.fast_mode = true;
1225
1352
  else
@@ -1295,7 +1422,28 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
1295
1422
  resume: resume ? { session_id: resume.session_id } : null,
1296
1423
  workerOutOffset,
1297
1424
  fastMode,
1425
+ ...(profile
1426
+ ? {
1427
+ profile: {
1428
+ id: profile.id,
1429
+ hash: profile.contentHash,
1430
+ ignoredFields: ignoredProfileFields,
1431
+ ...(profileRuntime?.settingsPath !== undefined
1432
+ ? { settingsPath: profileRuntime.settingsPath }
1433
+ : {}),
1434
+ },
1435
+ }
1436
+ : {}),
1298
1437
  });
1438
+ if (profile !== undefined) {
1439
+ appendWorkerProfileEvent(runDir, {
1440
+ profile_id: profile.id,
1441
+ profile_hash: profile.contentHash,
1442
+ changed: profileChanged,
1443
+ provider: lane.provider,
1444
+ ignored_fields: ignoredProfileFields,
1445
+ });
1446
+ }
1299
1447
  }
1300
1448
  catch (err) {
1301
1449
  if (workerPid !== undefined)
@@ -1355,30 +1503,59 @@ export function spawnEnvelopeRepair(hqRoot, lane, input, deps = {}) {
1355
1503
  const logsDir = path.join(runDir, "logs");
1356
1504
  ensureDir(logsDir);
1357
1505
  const config = loadLanesConfig(hqRoot);
1506
+ const profile = lane.worker_profile
1507
+ ? loadWorkerProfile(hqRoot, lane.worker_profile)
1508
+ : undefined;
1509
+ const profileDefaultsApply = profile !== undefined && profile.meta.provider === lane.provider;
1510
+ const profileModel = profileDefaultsApply ? profile.meta.model : undefined;
1511
+ const profileEffort = profileDefaultsApply ? profile.meta.effort : undefined;
1512
+ const ignoredProfileFields = profile
1513
+ ? ignoredWorkerProfileFields(lane.provider)
1514
+ : [];
1515
+ const profileRuntime = profile
1516
+ ? materializeWorkerProfile(profile, runDir)
1517
+ : undefined;
1518
+ const profileLaunch = profile
1519
+ ? {
1520
+ systemPrompt: workerProfilePrompt(profile),
1521
+ ...(profileRuntime?.settingsPath !== undefined
1522
+ ? { settingsPath: profileRuntime.settingsPath }
1523
+ : {}),
1524
+ ignoredFields: ignoredProfileFields,
1525
+ }
1526
+ : undefined;
1527
+ const pinnedModel = lane.model_source === undefined || lane.model_source === "cli"
1528
+ ? lane.model
1529
+ : undefined;
1358
1530
  const resolved = resolveAccount(lane.provider, lane.account, config, {
1359
1531
  hqRoot,
1360
1532
  existsSync,
1361
- model: lane.model,
1533
+ model: pinnedModel ?? profileModel,
1362
1534
  });
1363
1535
  const autoCompactTokens = lane.provider === "claude"
1364
1536
  ? providerAutoCompactTokens(config.providers.claude)
1365
1537
  : undefined;
1366
- const childEnv = workerEnv(env, { envVar: resolved.envVar, home: resolved.home }, lane.lane_id, hqRoot, autoCompactTokens);
1538
+ const childEnv = workerEnv(env, { envVar: resolved.envVar, home: resolved.home }, lane.lane_id, hqRoot, autoCompactTokens, lane.mesh_company ?? lane.company ?? undefined);
1367
1539
  const accountCfg = config.providers[lane.provider].accounts[resolved.id];
1368
1540
  const model = resolveLaneModel({
1369
- cliModel: lane.model,
1541
+ cliModel: pinnedModel,
1542
+ profileModel,
1370
1543
  accountModel: accountCfg?.model,
1371
1544
  providerModel: config.providers[lane.provider].model,
1372
1545
  });
1373
1546
  const effort = resolveLaneEffort({
1374
1547
  cliEffort: lane.effort_source === "cli" ? lane.effort : undefined,
1548
+ profileEffort,
1375
1549
  accountEffort: accountCfg?.effort,
1376
1550
  providerEffort: config.providers[lane.provider].effort,
1377
1551
  model,
1378
1552
  });
1379
- const fastMode = lane.provider === "codex" &&
1380
- model !== undefined &&
1381
- (config.providers.codex.fast_mode_models ?? []).includes(model);
1553
+ const fastMode = lane.provider === "codex"
1554
+ ? profileDefaultsApply
1555
+ ? profile?.meta.fast_mode === true
1556
+ : model !== undefined &&
1557
+ (config.providers.codex.fast_mode_models ?? []).includes(model)
1558
+ : false;
1382
1559
  const repairPrompt = envelopeRepairPrompt({
1383
1560
  envelopePath: input.envelopePath,
1384
1561
  error: input.error,
@@ -1392,6 +1569,7 @@ export function spawnEnvelopeRepair(hqRoot, lane, input, deps = {}) {
1392
1569
  model,
1393
1570
  effort: effort.effort,
1394
1571
  fastMode,
1572
+ profile: profileLaunch,
1395
1573
  ...(mode === "resume" && sessionId !== null
1396
1574
  ? { resume: { session_id: sessionId, prompt: repairPrompt } }
1397
1575
  : { freshTurnPrompt: repairPrompt }),
@@ -1418,9 +1596,55 @@ export function spawnEnvelopeRepair(hqRoot, lane, input, deps = {}) {
1418
1596
  resume: mode === "resume" && sessionId !== null ? { session_id: sessionId } : null,
1419
1597
  workerOutOffset,
1420
1598
  fastMode,
1599
+ ...(profile
1600
+ ? {
1601
+ profile: {
1602
+ id: profile.id,
1603
+ hash: profile.contentHash,
1604
+ ignoredFields: ignoredProfileFields,
1605
+ ...(profileRuntime?.settingsPath !== undefined
1606
+ ? { settingsPath: profileRuntime.settingsPath }
1607
+ : {}),
1608
+ },
1609
+ }
1610
+ : {}),
1421
1611
  });
1612
+ if (profile !== undefined) {
1613
+ appendWorkerProfileEvent(runDir, {
1614
+ profile_id: profile.id,
1615
+ profile_hash: profile.contentHash,
1616
+ changed: lane.worker_profile_hash !== profile.contentHash,
1617
+ provider: lane.provider,
1618
+ ignored_fields: ignoredProfileFields,
1619
+ });
1620
+ }
1422
1621
  retryUpdateLane(hqRoot, lane.lane_id, "spawn", (current) => {
1423
1622
  const next = { ...current };
1623
+ if (profile !== undefined) {
1624
+ next.worker_profile_hash = profile.contentHash;
1625
+ next.worker_profile_ignored = [...ignoredProfileFields];
1626
+ if (model !== undefined) {
1627
+ next.model = model;
1628
+ next.model_source = pinnedModel !== undefined
1629
+ ? "cli"
1630
+ : profileDefaultsApply
1631
+ ? "profile"
1632
+ : accountCfg?.model !== undefined
1633
+ ? "account"
1634
+ : config.providers[lane.provider].model !== undefined
1635
+ ? "provider"
1636
+ : "none";
1637
+ }
1638
+ else {
1639
+ next.model = undefined;
1640
+ next.model_source = "none";
1641
+ }
1642
+ next.effort_source = effort.source;
1643
+ if (effort.effort !== undefined)
1644
+ next.effort = effort.effort;
1645
+ else
1646
+ delete next.effort;
1647
+ }
1424
1648
  if (fastMode)
1425
1649
  next.fast_mode = true;
1426
1650
  else
@@ -268,6 +268,15 @@ function parseOptionalCompanyField(raw, filePath) {
268
268
  requireString(raw.company.slug, "company.slug");
269
269
  requireString(raw.company.uid, "company.uid");
270
270
  }
271
+ function parseOptionalMeshCompanyField(raw, filePath) {
272
+ if (raw.mesh_company === undefined)
273
+ return;
274
+ if (!isPlainObject(raw.mesh_company)) {
275
+ throw new LaneSchemaError("mesh_company must be an object", { path: filePath });
276
+ }
277
+ requireString(raw.mesh_company.slug, "mesh_company.slug");
278
+ requireString(raw.mesh_company.uid, "mesh_company.uid");
279
+ }
271
280
  export function parseLaneRecord(raw, filePath) {
272
281
  if (!isPlainObject(raw)) {
273
282
  throw new LaneSchemaError("Lane record must be a JSON object", {
@@ -294,6 +303,7 @@ export function parseLaneRecord(raw, filePath) {
294
303
  throw new LaneSchemaError(`Unknown provider ${JSON.stringify(raw.provider)}`, { path: filePath, value: raw.provider });
295
304
  }
296
305
  parseOptionalCompanyField(raw, filePath);
306
+ parseOptionalMeshCompanyField(raw, filePath);
297
307
  if (!isPlainObject(raw.senior)) {
298
308
  throw new LaneSchemaError("senior must be an object", { path: filePath });
299
309
  }