oh-my-opencode 4.16.1 → 4.16.3

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.
Files changed (151) hide show
  1. package/.agents/skills/pre-publish-review/SKILL.md +3 -0
  2. package/README.md +2 -2
  3. package/dist/agents/hephaestus/agent.d.ts +1 -1
  4. package/dist/agents/hephaestus/gpt-5-6.d.ts +2 -0
  5. package/dist/agents/momus-gpt-5-6.d.ts +1 -0
  6. package/dist/agents/types.d.ts +2 -0
  7. package/dist/cli/index.js +800 -536
  8. package/dist/cli-node/index.js +800 -536
  9. package/dist/features/background-agent/error-classifier.d.ts +5 -0
  10. package/dist/features/background-agent/parent-wake-dedupe.d.ts +1 -0
  11. package/dist/features/background-agent/parent-wake-flush-runner.d.ts +2 -0
  12. package/dist/features/background-agent/parent-wake-prompt-dispatch.d.ts +1 -0
  13. package/dist/features/builtin-commands/templates/refactor-sections/intro-and-analysis.d.ts +1 -1
  14. package/dist/features/builtin-commands/templates/remove-ai-slops.d.ts +1 -1
  15. package/dist/index.js +464 -54
  16. package/dist/shared/live-server-route.d.ts +1 -1
  17. package/dist/skills/review-work/SKILL.md +4 -2
  18. package/dist/skills/start-work/SKILL.md +2 -0
  19. package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
  20. package/dist/skills/ulw-research/SKILL.md +5 -3
  21. package/dist/skills/visual-qa/SKILL.md +1 -1
  22. package/dist/tui.js +125 -13
  23. package/package.json +13 -13
  24. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  25. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +398 -246
  26. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  27. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  28. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  29. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  30. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  31. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  32. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  33. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  34. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  35. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  36. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  37. package/packages/omo-codex/plugin/components/rules/bundled-rules/{hephaestus.md → hephaestus/gpt-5.5.md} +1 -1
  38. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +93 -0
  39. package/packages/omo-codex/plugin/components/rules/dist/cli.js +43 -24
  40. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  41. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/rules/src/codex-hook.ts +2 -1
  43. package/packages/omo-codex/plugin/components/rules/src/dynamic-target-fingerprints.ts +15 -6
  44. package/packages/omo-codex/plugin/components/rules/src/rules-engine-factory.ts +7 -2
  45. package/packages/omo-codex/plugin/components/rules/src/static-injection.ts +2 -2
  46. package/packages/omo-codex/plugin/components/rules/test/codex-hook.test.ts +32 -0
  47. package/packages/omo-codex/plugin/components/rules/test/hephaestus-model-variant.test.ts +106 -0
  48. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +1 -1
  49. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  50. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +1 -1
  52. package/packages/omo-codex/plugin/components/teammode/AGENTS.md +12 -9
  53. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  54. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  55. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +136 -76
  56. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-guide.mjs +94 -23
  57. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +94 -14
  58. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-transport.mjs +55 -0
  59. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +72 -18
  60. package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +23 -0
  61. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  62. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  63. package/packages/omo-codex/plugin/components/ultrawork/agents/explorer.toml +24 -41
  64. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +1 -1
  65. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +1 -1
  66. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-executor.toml +2 -2
  67. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +1 -1
  68. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-qa-executor.toml +1 -1
  69. package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +21 -49
  70. package/packages/omo-codex/plugin/components/ultrawork/agents/metis.toml +17 -29
  71. package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +20 -50
  72. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +18 -39
  73. package/packages/omo-codex/plugin/components/ultrawork/directive.md +1 -0
  74. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  75. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  76. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +1 -0
  77. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +2 -0
  78. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +3 -1
  79. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +1 -0
  80. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
  81. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  82. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +2 -0
  83. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +1 -1
  84. package/packages/omo-codex/plugin/components/ulw-loop/test/skill-contract.test.ts +1 -1
  85. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  86. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  87. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  88. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  89. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  90. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  91. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  92. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  93. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  94. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  95. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  96. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  97. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  98. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  99. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  100. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  101. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  102. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  103. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  104. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  105. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  106. package/packages/omo-codex/plugin/package-lock.json +13 -13
  107. package/packages/omo-codex/plugin/package.json +1 -1
  108. package/packages/omo-codex/plugin/scripts/auto-update.mjs +73 -19
  109. package/packages/omo-codex/plugin/scripts/entry-guard.mjs +26 -0
  110. package/packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs +3 -1
  111. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +221 -20
  112. package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +71 -3
  113. package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +35 -7
  114. package/packages/omo-codex/plugin/scripts/migrate-omo-sot.mjs +2 -2
  115. package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +2 -2
  116. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +7 -5
  117. package/packages/omo-codex/plugin/scripts/sync-version.mjs +4 -2
  118. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +2 -2
  119. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +2 -2
  120. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +2 -2
  121. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +2 -2
  122. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +2 -2
  123. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +136 -76
  124. package/packages/omo-codex/plugin/skills/teammode/scripts/team-guide.mjs +94 -23
  125. package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +94 -14
  126. package/packages/omo-codex/plugin/skills/teammode/scripts/team-transport.mjs +55 -0
  127. package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +72 -18
  128. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +1 -0
  129. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +2 -0
  130. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +1 -1
  131. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +2 -0
  132. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +3 -1
  133. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +3 -3
  134. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +3 -3
  135. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +47 -1
  136. package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +1 -1
  137. package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +4 -2
  138. package/packages/omo-codex/plugin/test/auto-update-restart-notice.test.mjs +33 -0
  139. package/packages/omo-codex/plugin/test/auto-update.test.mjs +35 -1
  140. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +412 -19
  141. package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +71 -0
  142. package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +65 -0
  143. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +13 -4
  144. package/packages/omo-codex/plugin/test/teammode-transport.test.mjs +345 -0
  145. package/packages/omo-codex/scripts/install-dist/install-local.mjs +436 -284
  146. package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +111 -1
  147. package/packages/shared-skills/skills/review-work/SKILL.md +4 -2
  148. package/packages/shared-skills/skills/start-work/SKILL.md +2 -0
  149. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
  150. package/packages/shared-skills/skills/ulw-research/SKILL.md +5 -3
  151. package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
@@ -16,9 +16,50 @@ const agentSchemaKeys = new Set([
16
16
  ]);
17
17
 
18
18
  const lazycodexAgentInvariants = new Map([
19
+ [
20
+ "explorer.toml",
21
+ {
22
+ model: "gpt-5.6-terra",
23
+ effort: "medium",
24
+ includes: [/Read-only/, /working tree/, /rg/],
25
+ },
26
+ ],
27
+ [
28
+ "librarian.toml",
29
+ {
30
+ model: "gpt-5.6-terra",
31
+ effort: "medium",
32
+ includes: [/Read-only/, /SHA-pinned GitHub permalink/, /external/],
33
+ },
34
+ ],
35
+ [
36
+ "metis.toml",
37
+ {
38
+ model: "gpt-5.6-sol",
39
+ effort: "high",
40
+ includes: [/pre-planning analyst/i, /contradictions/, /Read-only/],
41
+ },
42
+ ],
43
+ [
44
+ "momus.toml",
45
+ {
46
+ model: "gpt-5.6-sol",
47
+ effort: "ultra",
48
+ includes: [/plan reviewer/i, /OKAY, ITERATE, or REJECT/, /Read-only/],
49
+ },
50
+ ],
51
+ [
52
+ "plan.toml",
53
+ {
54
+ model: "gpt-5.6-sol",
55
+ effort: "xhigh",
56
+ includes: [/strategic planning consultant/i, /\.omo\/plans\/<slug>\.md/, /never implements/i],
57
+ },
58
+ ],
19
59
  [
20
60
  "lazycodex-executor.toml",
21
61
  {
62
+ model: "gpt-5.6-sol",
22
63
  effort: "high",
23
64
  includes: [/EVIDENCE_RECORDED: <path>/, /scenario/i, /artifact/i],
24
65
  },
@@ -26,6 +67,7 @@ const lazycodexAgentInvariants = new Map([
26
67
  [
27
68
  "lazycodex-clone-fidelity-reviewer.toml",
28
69
  {
70
+ model: "gpt-5.6-sol",
29
71
  effort: "xhigh",
30
72
  includes: [/recommendation/, /blockers/, /\.omo\/evidence\/<goal>-clone-fidelity\.md/],
31
73
  },
@@ -33,6 +75,7 @@ const lazycodexAgentInvariants = new Map([
33
75
  [
34
76
  "lazycodex-code-reviewer.toml",
35
77
  {
78
+ model: "gpt-5.6-sol",
36
79
  effort: "xhigh",
37
80
  includes: [/codeQualityStatus/, /recommendation/, /\.omo\/evidence\/<goal>-code-review\.md/],
38
81
  },
@@ -40,6 +83,7 @@ const lazycodexAgentInvariants = new Map([
40
83
  [
41
84
  "lazycodex-qa-executor.toml",
42
85
  {
86
+ model: "gpt-5.6-terra",
43
87
  effort: "medium",
44
88
  includes: [/not_applicable/, /surfaceEvidence/, /adversarialCases/],
45
89
  },
@@ -47,6 +91,7 @@ const lazycodexAgentInvariants = new Map([
47
91
  [
48
92
  "lazycodex-gate-reviewer.toml",
49
93
  {
94
+ model: "gpt-5.6-sol",
50
95
  effort: "xhigh",
51
96
  includes: [/APPROVE\/REJECT/, /blockers/, /\.omo\/evidence\/<goal>-gate-review\.md/],
52
97
  },
@@ -109,7 +154,8 @@ test("#given lazycodex agent prompts #when inspected #then each role pins model
109
154
  for (const [fileName, invariant] of lazycodexAgentInvariants) {
110
155
  const prompt = await readFile(join(agentsDir, fileName), "utf8");
111
156
 
112
- assert.match(prompt, /^model\s*=\s*"gpt-5\.5"$/m);
157
+ const escapedModel = invariant.model.replace(/\./g, "\\.");
158
+ assert.match(prompt, new RegExp(`^model\\s*=\\s*"${escapedModel}"$`, "m"));
113
159
  assert.match(prompt, new RegExp(`^model_reasoning_effort\\s*=\\s*"${invariant.effort}"$`, "m"));
114
160
  assert.doesNotMatch(prompt, /^tools\s*=/m);
115
161
  assert.doesNotMatch(prompt, /^blocking\s*=/m);
@@ -88,7 +88,7 @@ export function findSpawnAgentCallsWithoutForkContextFalse(content) {
88
88
  const regex = /spawn_agent\(([^)]*)\)/g;
89
89
  for (const match of content.matchAll(regex)) {
90
90
  const call = match[0];
91
- if (!/"fork_context"\s*:\s*false|fork_context:\s*false|fork_context=false/.test(call)) {
91
+ if (!/"fork_context"\s*:\s*false|fork_context:\s*false|fork_context=false|"fork_turns"\s*:\s*"none"|fork_turns:\s*"none"|fork_turns="none"/.test(call)) {
92
92
  missingForkContext.push(call);
93
93
  }
94
94
  }
@@ -34,7 +34,8 @@ test('#given synced skills and bundled rules #when role-specific agents are spaw
34
34
  const promptFiles = skillEntries
35
35
  .filter((entry) => entry.isDirectory())
36
36
  .map((entry) => join(skillsDir, entry.name, "SKILL.md"));
37
- promptFiles.push(join(root, "components", "rules", "bundled-rules", "hephaestus.md"));
37
+ promptFiles.push(join(root, "components", "rules", "bundled-rules", "hephaestus", "gpt-5.5.md"));
38
+ promptFiles.push(join(root, "components", "rules", "bundled-rules", "hephaestus", "gpt-5.6.md"));
38
39
 
39
40
  const missingForkContext = [];
40
41
  for (const promptPath of promptFiles) {
@@ -53,7 +54,8 @@ test("#given long-running orchestration prompts #when waiting on child agents #t
53
54
  join(root, "skills", "ulw-loop", "references", "full-workflow.md"),
54
55
  join(root, "skills", "review-work", "SKILL.md"),
55
56
  join(root, "skills", "start-work", "SKILL.md"),
56
- join(root, "components", "rules", "bundled-rules", "hephaestus.md"),
57
+ join(root, "components", "rules", "bundled-rules", "hephaestus", "gpt-5.5.md"),
58
+ join(root, "components", "rules", "bundled-rules", "hephaestus", "gpt-5.6.md"),
57
59
  ];
58
60
 
59
61
  const missingLivenessGuidance = [];
@@ -224,3 +224,36 @@ test("#given completed pending update #when hook session-start runs as CLI #then
224
224
  assert.equal(repeat.status, 0);
225
225
  assert.equal(repeat.stdout, "");
226
226
  });
227
+
228
+ test("#given the script reached through a symlinked directory #when hook session-start runs as CLI #then the entry guard still fires", async () => {
229
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-restart-notice-symlink-"));
230
+ const env = autoUpdateEnv(root, {
231
+ LAZYCODEX_CURRENT_VERSION: "1.0.1",
232
+ LAZYCODEX_LATEST_VERSION: "1.0.1",
233
+ LAZYCODEX_CONFIG_MIGRATION_DISABLED: "1",
234
+ });
235
+ await writeFile(env.LAZYCODEX_AUTO_UPDATE_STATE_PATH, JSON.stringify({
236
+ lastCheckedAt: Date.now() - 1_000,
237
+ lastStatus: "success",
238
+ pendingNotice: { fromVersion: "1.0.0", toVersion: "1.0.1", startedAt: 1 },
239
+ }));
240
+ // Simulate a symlinked plugin cache dir: argv[1] spells the symlink, while
241
+ // import.meta.url resolves to the real path. Seen in the wild; the old
242
+ // `pathToFileURL(process.argv[1])` guard never matched and the hook was a
243
+ // silent no-op.
244
+ const { symlink } = await import("node:fs/promises");
245
+ const { dirname } = await import("node:path");
246
+ const linkDir = join(root, "linked-scripts");
247
+ await symlink(dirname(SCRIPT_PATH), linkDir, "dir");
248
+ const linkedScript = join(linkDir, "auto-update.mjs");
249
+
250
+ const result = spawnSync(process.execPath, [linkedScript, "hook", "session-start"], {
251
+ encoding: "utf8",
252
+ env: { ...process.env, ...env },
253
+ });
254
+
255
+ assert.equal(result.status, 0);
256
+ const lines = result.stdout.split("\n").filter((line) => line.length > 0);
257
+ assert.equal(lines.length, 1, `expected one SessionStart JSON line, got stdout=${JSON.stringify(result.stdout)} stderr=${JSON.stringify(result.stderr)}`);
258
+ assert.equal(JSON.parse(lines[0]).hookSpecificOutput.hookEventName, "SessionStart");
259
+ });
@@ -2,9 +2,10 @@ import assert from "node:assert/strict";
2
2
  import { mkdir, mkdtemp, readFile, symlink, utimes, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
+ import { PassThrough } from "node:stream";
5
6
  import test from "node:test";
6
7
 
7
- import { resolveAutoUpdatePlan, resolveLazyCodexUpdatePlan, runAutoUpdateCheck } from "../scripts/auto-update.mjs";
8
+ import { readSessionModelFromStdin, resolveAutoUpdatePlan, resolveLazyCodexUpdatePlan, runAutoUpdateCheck } from "../scripts/auto-update.mjs";
8
9
  import { detectInstallFlow } from "../scripts/install-flow.mjs";
9
10
  import { resolveSpawnInvocation } from "../scripts/spawn-command.mjs";
10
11
 
@@ -20,6 +21,12 @@ function autoUpdateEnv(root, extra = {}) {
20
21
  };
21
22
  }
22
23
 
24
+ function stdinFrom(payload) {
25
+ const stream = new PassThrough();
26
+ stream.end(payload);
27
+ return stream;
28
+ }
29
+
23
30
  test("#given auto update is disabled #when resolving plan #then no command is scheduled", () => {
24
31
  const plan = resolveAutoUpdatePlan({
25
32
  env: { LAZYCODEX_AUTO_UPDATE_DISABLED: "1" },
@@ -31,6 +38,33 @@ test("#given auto update is disabled #when resolving plan #then no command is sc
31
38
  assert.equal(plan.reason, "disabled");
32
39
  });
33
40
 
41
+ test("#given SessionStart payload with model #when reading stdin #then returns the model", async () => {
42
+ const payload = JSON.stringify({
43
+ hook_event_name: "SessionStart",
44
+ session_id: "s-1",
45
+ cwd: "/tmp",
46
+ model: "gpt-5.6-terra",
47
+ permission_mode: "default",
48
+ source: "startup",
49
+ });
50
+
51
+ assert.equal(await readSessionModelFromStdin(stdinFrom(payload)), "gpt-5.6-terra");
52
+ });
53
+
54
+ test("#given empty or malformed stdin #when reading session model #then returns null", async () => {
55
+ assert.equal(await readSessionModelFromStdin(stdinFrom("")), null);
56
+ assert.equal(await readSessionModelFromStdin(stdinFrom("not json")), null);
57
+ assert.equal(await readSessionModelFromStdin(stdinFrom(JSON.stringify({ model: " " }))), null);
58
+ assert.equal(await readSessionModelFromStdin(stdinFrom(JSON.stringify({ session_id: "s-1" }))), null);
59
+ assert.equal(await readSessionModelFromStdin(null), null);
60
+ });
61
+
62
+ test("#given a TTY stdin #when reading session model #then returns null without waiting", async () => {
63
+ const stream = stdinFrom("{}");
64
+ stream.isTTY = true;
65
+ assert.equal(await readSessionModelFromStdin(stream), null);
66
+ });
67
+
34
68
  test("#given stale state #when resolving plan #then installer update command is scheduled", () => {
35
69
  const plan = resolveAutoUpdatePlan({
36
70
  env: { LAZYCODEX_CURRENT_VERSION: "1.0.0", LAZYCODEX_LATEST_VERSION: "1.0.1" },