@indigoai-us/hq-cli 5.345.65 → 5.345.67

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 CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.67] — 2026-10-09
6
+
7
+ - Persist the Codex SessionStart model and effort for conduct lanes, with safe fallback defaults.
8
+
9
+ - Native core families go back to their strict readers from 5.345.65. In 5.345.66, a process with no company context ignored the flag registry and ran every native family, so the registry kill switch could not turn a family off on most machines.
10
+
11
+ ## [5.345.66] — 2026-10-09
12
+ - Native core families now run by default when the machine has no flag snapshot (including first-run and offline cases); explicit false in a fresh or stale snapshot remains the kill switch. Only the seven approved core-native families use default-on readers. Delegate and worker-skills keep strict default-off readers pending parity follow-ups, and other fast-route families such as hook-batch also remain on the strict reader.
13
+ - The Windows fast-route shell fallback now converts bundled script paths to Git Bash paths, so the family kill switch can reach its shell companion.
14
+
5
15
  ## [5.345.65] — 2026-10-09
6
16
  - Reject unsafe lane and child ids before they reach inbox or state paths.
7
17
 
@@ -6,6 +6,74 @@ import { randomUUID } from "node:crypto";
6
6
  import { resolveLanesHqRoot } from "../lib/lanes/hq-root.js";
7
7
  import { readLanesRegistryFlagFailOpen } from "../lib/lanes/registry-flag.js";
8
8
  import { LANE_CONDUCT_SESSION_START_FLAG } from "../lib/lanes/flag-keys.js";
9
+ const CODEX_DEFAULT_KEYS = ["conduct_engine", "conduct_child_model", "conduct_child_effort", "conduct_default_source"];
10
+ function readSessionMetaValue(text, key) {
11
+ const line = text.split(/\r?\n/).find((entry) => new RegExp(`^\\s*${key}:`).test(entry));
12
+ if (!line)
13
+ return "";
14
+ const value = line.replace(/^\s*[^:]+:\s*/, "");
15
+ return value.replace(/^"|"$/g, "");
16
+ }
17
+ function writeCodexSessionDefaults(root, env, payload, debug) {
18
+ if (env.HQ_HARNESS !== "codex")
19
+ return;
20
+ const sessionId = payload.session_id ?? payload.sessionId;
21
+ if (!sessionId || !/^[A-Za-z0-9._-]+$/.test(sessionId) || sessionId === "." || sessionId === "..")
22
+ return;
23
+ let model = typeof payload.model === "string" ? payload.model : "";
24
+ if (/\s/.test(model))
25
+ model = "";
26
+ let effort = payload.reasoning_effort ?? payload.reasoningEffort ?? payload.effort ?? "";
27
+ if (typeof effort !== "string" || !/^[a-z]+$/.test(effort))
28
+ effort = "";
29
+ try {
30
+ const sessionDir = path.join(root, "workspace", "sessions", sessionId);
31
+ const metaPath = path.join(sessionDir, "meta.yaml");
32
+ fs.mkdirSync(sessionDir, { recursive: true });
33
+ let text = "";
34
+ try {
35
+ text = fs.readFileSync(metaPath, "utf8");
36
+ }
37
+ catch (error) {
38
+ if (error.code !== "ENOENT")
39
+ throw error;
40
+ }
41
+ if (CODEX_DEFAULT_KEYS.some((key) => readSessionMetaValue(text, key) !== ""))
42
+ return;
43
+ if (!text) {
44
+ text = `session_id: ${sessionId}\nstarted_at: "${new Date().toISOString().replace(/\.\d{3}Z$/, "Z")}"\nsenior: user\n`;
45
+ }
46
+ const defaults = {
47
+ conduct_engine: "codex",
48
+ conduct_child_model: model || undefined,
49
+ conduct_child_effort: effort || undefined,
50
+ conduct_default_source: model ? "codex-session" : "codex-fallback",
51
+ };
52
+ for (const key of CODEX_DEFAULT_KEYS) {
53
+ const value = defaults[key];
54
+ if (value === undefined)
55
+ continue;
56
+ const matcher = new RegExp(`^\\s*${key}:.*(?:\\r?\\n|$)`, "m");
57
+ const line = `${key}: ${value}\n`;
58
+ text = matcher.test(text) ? text.replace(matcher, line) : `${text}${text.endsWith("\n") || !text ? "" : "\n"}${line}`;
59
+ }
60
+ const tempPath = path.join(sessionDir, `.meta-${process.pid}-${randomUUID()}.tmp`);
61
+ try {
62
+ fs.writeFileSync(tempPath, text, { mode: 0o600 });
63
+ fs.renameSync(tempPath, metaPath);
64
+ }
65
+ finally {
66
+ try {
67
+ fs.rmSync(tempPath, { force: true });
68
+ }
69
+ catch { /* best-effort temp cleanup */ }
70
+ }
71
+ }
72
+ catch (error) {
73
+ const kind = error instanceof Error ? error.name : "Error";
74
+ debug(`hq lanes session-start: Codex metadata bootstrap failed (${kind})`);
75
+ }
76
+ }
9
77
  function enabledFromSettings(root) {
10
78
  const personal = path.join(root, "personal", "settings", "orchestrator.yaml");
11
79
  const settings = fs.existsSync(personal) ? personal : path.join(root, "core", "settings", "orchestrator.yaml");
@@ -155,11 +223,10 @@ export async function runLaneSessionStart(options = {}) {
155
223
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
156
224
  const input = Buffer.concat(chunks).toString("utf8");
157
225
  let source = "startup";
158
- let payloadSessionId;
226
+ let payload = {};
159
227
  try {
160
- const payload = JSON.parse(input);
228
+ payload = JSON.parse(input);
161
229
  source = payload.source || "startup";
162
- payloadSessionId = payload.session_id;
163
230
  }
164
231
  catch { /* malformed hook input uses startup default */ }
165
232
  if (source !== "startup")
@@ -179,7 +246,7 @@ export async function runLaneSessionStart(options = {}) {
179
246
  if (!flagEnabled)
180
247
  return;
181
248
  (options.startSweep ?? ((r, e) => startSessionStartOrphanSweep(r, e, spawn)))(root, env);
182
- if (sessionWasDisabled(root, env, payloadSessionId))
249
+ if (sessionWasDisabled(root, env, payload.session_id ?? payload.sessionId))
183
250
  return;
184
251
  const autoConduct = env.HQ_AUTO_CONDUCT ?? "";
185
252
  const forceOn = /^(1|true|on|yes)$/i.test(autoConduct);
@@ -192,6 +259,7 @@ export async function runLaneSessionStart(options = {}) {
192
259
  enabled = true;
193
260
  if (!enabled)
194
261
  return;
262
+ writeCodexSessionDefaults(root, env, payload, debug);
195
263
  const core = path.join(root, ".claude", "skills", "conduct", "conductor-core.md");
196
264
  if (!fs.existsSync(core))
197
265
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.345.65",
3
+ "version": "5.345.67",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {