oh-my-codex 0.14.0 → 0.14.1
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/Cargo.lock +5 -5
- package/Cargo.toml +1 -1
- package/README.md +14 -8
- package/crates/omx-explore/src/main.rs +94 -1
- package/crates/omx-sparkshell/src/codex_bridge.rs +59 -12
- package/crates/omx-sparkshell/tests/execution.rs +48 -0
- package/dist/cli/__tests__/explore.test.js +33 -1
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +11 -2
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +5 -0
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.js +78 -25
- package/dist/cli/__tests__/question.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +32 -7
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +8 -6
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/sparkshell-cli.test.js +23 -0
- package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +65 -5
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +360 -26
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +18 -3
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +2 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +7 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +25 -3
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/sparkshell.d.ts.map +1 -1
- package/dist/cli/sparkshell.js +11 -1
- package/dist/cli/sparkshell.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +159 -394
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +3 -1
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/cli/update.d.ts +37 -9
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +204 -26
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +51 -14
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +35 -10
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/generator.d.ts +1 -0
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +61 -7
- package/dist/config/generator.js.map +1 -1
- package/dist/hooks/__tests__/code-review-skill-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.js +56 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/deep-interview-contract.test.js +29 -0
- package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.js +43 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.js +38 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.js +16 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +5 -24
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +126 -0
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +1 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +3 -11
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-server.d.ts +17 -0
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/state-server.js +37 -0
- package/dist/mcp/state-server.js.map +1 -1
- package/dist/notifications/__tests__/index.test.js +0 -3
- package/dist/notifications/__tests__/index.test.js.map +1 -1
- package/dist/notifications/__tests__/session-status.test.js +90 -0
- package/dist/notifications/__tests__/session-status.test.js.map +1 -1
- package/dist/notifications/session-status.d.ts +2 -0
- package/dist/notifications/session-status.d.ts.map +1 -1
- package/dist/notifications/session-status.js +19 -4
- package/dist/notifications/session-status.js.map +1 -1
- package/dist/question/__tests__/deep-interview.test.js +10 -0
- package/dist/question/__tests__/deep-interview.test.js.map +1 -1
- package/dist/question/__tests__/renderer.test.js +157 -7
- package/dist/question/__tests__/renderer.test.js.map +1 -1
- package/dist/question/__tests__/state.test.js +21 -1
- package/dist/question/__tests__/state.test.js.map +1 -1
- package/dist/question/deep-interview.d.ts +3 -0
- package/dist/question/deep-interview.d.ts.map +1 -1
- package/dist/question/deep-interview.js +18 -1
- package/dist/question/deep-interview.js.map +1 -1
- package/dist/question/renderer.d.ts +3 -1
- package/dist/question/renderer.d.ts.map +1 -1
- package/dist/question/renderer.js +75 -13
- package/dist/question/renderer.js.map +1 -1
- package/dist/runtime/__tests__/run-outcome.test.js +38 -0
- package/dist/runtime/__tests__/run-outcome.test.js.map +1 -1
- package/dist/runtime/__tests__/run-state.test.d.ts +2 -0
- package/dist/runtime/__tests__/run-state.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/run-state.test.js +37 -0
- package/dist/runtime/__tests__/run-state.test.js.map +1 -0
- package/dist/runtime/run-loop.d.ts +5 -1
- package/dist/runtime/run-loop.d.ts.map +1 -1
- package/dist/runtime/run-loop.js +8 -3
- package/dist/runtime/run-loop.js.map +1 -1
- package/dist/runtime/run-outcome.d.ts +18 -0
- package/dist/runtime/run-outcome.d.ts.map +1 -1
- package/dist/runtime/run-outcome.js +156 -7
- package/dist/runtime/run-outcome.js.map +1 -1
- package/dist/runtime/run-state.d.ts +5 -1
- package/dist/runtime/run-state.d.ts.map +1 -1
- package/dist/runtime/run-state.js +13 -3
- package/dist/runtime/run-state.js.map +1 -1
- package/dist/runtime/terminal-lifecycle.d.ts +11 -0
- package/dist/runtime/terminal-lifecycle.d.ts.map +1 -0
- package/dist/runtime/terminal-lifecycle.js +52 -0
- package/dist/runtime/terminal-lifecycle.js.map +1 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js +346 -56
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/postinstall.test.d.ts +2 -0
- package/dist/scripts/__tests__/postinstall.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/postinstall.test.js +178 -0
- package/dist/scripts/__tests__/postinstall.test.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts +1 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +115 -56
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/postinstall.d.ts +22 -0
- package/dist/scripts/postinstall.d.ts.map +1 -0
- package/dist/scripts/postinstall.js +105 -0
- package/dist/scripts/postinstall.js.map +1 -0
- package/dist/state/__tests__/operations.test.js +18 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/team/__tests__/role-router.test.js +6 -0
- package/dist/team/__tests__/role-router.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +108 -2
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +18 -4
- package/dist/team/runtime.js.map +1 -1
- package/dist/utils/__tests__/dep-versions.test.js +25 -8
- package/dist/utils/__tests__/dep-versions.test.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +45 -0
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/paths.d.ts +2 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +22 -7
- package/dist/utils/paths.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/package.json +2 -1
- package/prompts/architect.md +4 -0
- package/prompts/code-reviewer.md +3 -0
- package/skills/code-review/SKILL.md +94 -28
- package/skills/deep-interview/SKILL.md +90 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +438 -64
- package/src/scripts/__tests__/postinstall.test.ts +210 -0
- package/src/scripts/codex-native-hook.ts +136 -53
- package/src/scripts/postinstall-bootstrap.js +23 -0
- package/src/scripts/postinstall.ts +161 -0
- package/templates/model-instructions/explore-lightweight-AGENTS.md +11 -0
- package/templates/model-instructions/sparkshell-lightweight-AGENTS.md +10 -0
package/Cargo.lock
CHANGED
|
@@ -32,11 +32,11 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
|
|
32
32
|
|
|
33
33
|
[[package]]
|
|
34
34
|
name = "omx-explore-harness"
|
|
35
|
-
version = "0.14.
|
|
35
|
+
version = "0.14.1"
|
|
36
36
|
|
|
37
37
|
[[package]]
|
|
38
38
|
name = "omx-mux"
|
|
39
|
-
version = "0.14.
|
|
39
|
+
version = "0.14.1"
|
|
40
40
|
dependencies = [
|
|
41
41
|
"serde",
|
|
42
42
|
"serde_json",
|
|
@@ -44,7 +44,7 @@ dependencies = [
|
|
|
44
44
|
|
|
45
45
|
[[package]]
|
|
46
46
|
name = "omx-runtime"
|
|
47
|
-
version = "0.14.
|
|
47
|
+
version = "0.14.1"
|
|
48
48
|
dependencies = [
|
|
49
49
|
"omx-mux",
|
|
50
50
|
"omx-runtime-core",
|
|
@@ -53,7 +53,7 @@ dependencies = [
|
|
|
53
53
|
|
|
54
54
|
[[package]]
|
|
55
55
|
name = "omx-runtime-core"
|
|
56
|
-
version = "0.14.
|
|
56
|
+
version = "0.14.1"
|
|
57
57
|
dependencies = [
|
|
58
58
|
"fs2",
|
|
59
59
|
"serde",
|
|
@@ -62,7 +62,7 @@ dependencies = [
|
|
|
62
62
|
|
|
63
63
|
[[package]]
|
|
64
64
|
name = "omx-sparkshell"
|
|
65
|
-
version = "0.14.
|
|
65
|
+
version = "0.14.1"
|
|
66
66
|
dependencies = [
|
|
67
67
|
"omx-mux",
|
|
68
68
|
]
|
package/Cargo.toml
CHANGED
package/README.md
CHANGED
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
[](https://discord.gg/PUwSMR9XNk)
|
|
13
13
|
|
|
14
14
|
**Website:** https://yeachan-heo.github.io/oh-my-codex-website/
|
|
15
|
+
|
|
15
16
|
**Docs:** [Getting Started](./docs/getting-started.html) · [Agents](./docs/agents.html) · [Skills](./docs/skills.html) · [Integrations](./docs/integrations.html) · [Demo](./DEMO.md) · [OpenClaw guide](./docs/openclaw-integration.md)
|
|
17
|
+
|
|
16
18
|
**Community:** [Discord](https://discord.gg/PUwSMR9XNk) — shared OMX/community server for oh-my-codex and related tooling.
|
|
17
19
|
|
|
18
20
|
OMX is a workflow layer for [OpenAI Codex CLI](https://github.com/openai/codex).
|
|
@@ -58,10 +60,11 @@ If you want the default OMX experience, start here:
|
|
|
58
60
|
|
|
59
61
|
```bash
|
|
60
62
|
npm install -g @openai/codex oh-my-codex
|
|
61
|
-
omx setup
|
|
62
63
|
omx --madmax --high
|
|
63
64
|
```
|
|
64
65
|
|
|
66
|
+
On a real `oh-my-codex` version bump, the global npm install now launches the interactive `omx setup` refresh automatically when a TTY is available. If npm scripts are skipped or the install is non-interactive, run `omx setup` manually or use `omx update` to force the same refresh path later.
|
|
67
|
+
|
|
65
68
|
Then work normally inside Codex:
|
|
66
69
|
|
|
67
70
|
```text
|
|
@@ -145,13 +148,14 @@ Most users should think of OMX as **better task routing + better workflow + bett
|
|
|
145
148
|
|
|
146
149
|
## Start here if you are new
|
|
147
150
|
|
|
148
|
-
1.
|
|
149
|
-
2.
|
|
150
|
-
3. Run
|
|
151
|
-
4.
|
|
152
|
-
5.
|
|
153
|
-
6. Use `$
|
|
154
|
-
7.
|
|
151
|
+
1. Install or update OMX with `npm install -g @openai/codex oh-my-codex`
|
|
152
|
+
2. Let the interactive `omx setup` refresh run automatically on real OMX version bumps, or run `omx setup` / `omx update` yourself when scripts are skipped or you want to rerun it
|
|
153
|
+
3. Run `omx doctor`
|
|
154
|
+
4. Run a real execution smoke test: `codex login status` and `omx exec --skip-git-repo-check -C . "Reply with exactly OMX-EXEC-OK"`
|
|
155
|
+
5. Launch with `omx --madmax --high`
|
|
156
|
+
6. Use `$deep-interview "..."` when the request or boundaries are still unclear
|
|
157
|
+
7. Use `$ralplan "..."` to approve the plan and review tradeoffs
|
|
158
|
+
8. Choose `$team` for coordinated parallel execution or `$ralph` for persistent completion loops
|
|
155
159
|
|
|
156
160
|
## Recommended workflow
|
|
157
161
|
|
|
@@ -190,6 +194,8 @@ These are operator/support surfaces:
|
|
|
190
194
|
- `omx setup` installs prompts, skills, AGENTS scaffolding, `.codex/config.toml`, and OMX-managed native Codex hooks in `.codex/hooks.json`
|
|
191
195
|
- setup refresh preserves non-OMX hook entries in `.codex/hooks.json` and only rewrites OMX-managed wrappers
|
|
192
196
|
- `omx uninstall` removes OMX-managed wrappers from `.codex/hooks.json` but keeps the file when user hooks remain
|
|
197
|
+
- `omx update` checks npm immediately, installs the newest global OMX build, then reruns the same interactive setup refresh path
|
|
198
|
+
- fresh OMX-managed `gpt-5.4` config seeding now recommends `model_context_window = 250000` and `model_auto_compact_token_limit = 200000`, but only when those keys are missing
|
|
193
199
|
- `omx doctor` verifies the install when something seems wrong; it does not prove that the active Codex profile can make an authenticated model call
|
|
194
200
|
- `omx hud --watch` is a monitoring/status surface, not the primary user workflow
|
|
195
201
|
|
|
@@ -26,6 +26,7 @@ struct Args {
|
|
|
26
26
|
cwd: PathBuf,
|
|
27
27
|
prompt: String,
|
|
28
28
|
prompt_file: PathBuf,
|
|
29
|
+
instructions_file: PathBuf,
|
|
29
30
|
spark_model: String,
|
|
30
31
|
fallback_model: String,
|
|
31
32
|
}
|
|
@@ -155,6 +156,7 @@ where
|
|
|
155
156
|
let mut cwd: Option<PathBuf> = None;
|
|
156
157
|
let mut prompt: Option<String> = None;
|
|
157
158
|
let mut prompt_file: Option<PathBuf> = None;
|
|
159
|
+
let mut instructions_file: Option<PathBuf> = None;
|
|
158
160
|
let mut spark_model: Option<String> = None;
|
|
159
161
|
let mut fallback_model: Option<String> = None;
|
|
160
162
|
|
|
@@ -166,6 +168,12 @@ where
|
|
|
166
168
|
"--prompt-file" => {
|
|
167
169
|
prompt_file = Some(PathBuf::from(next_required(&mut args, "--prompt-file")?))
|
|
168
170
|
}
|
|
171
|
+
"--instructions-file" => {
|
|
172
|
+
instructions_file = Some(PathBuf::from(next_required(
|
|
173
|
+
&mut args,
|
|
174
|
+
"--instructions-file",
|
|
175
|
+
)?))
|
|
176
|
+
}
|
|
169
177
|
"--model-spark" => spark_model = Some(next_required(&mut args, "--model-spark")?),
|
|
170
178
|
"--model-fallback" => {
|
|
171
179
|
fallback_model = Some(next_required(&mut args, "--model-fallback")?)
|
|
@@ -179,6 +187,8 @@ where
|
|
|
179
187
|
cwd: cwd.ok_or_else(|| format!("missing --cwd\n{}", usage()))?,
|
|
180
188
|
prompt: prompt.ok_or_else(|| format!("missing --prompt\n{}", usage()))?,
|
|
181
189
|
prompt_file: prompt_file.ok_or_else(|| format!("missing --prompt-file\n{}", usage()))?,
|
|
190
|
+
instructions_file: instructions_file
|
|
191
|
+
.ok_or_else(|| format!("missing --instructions-file\n{}", usage()))?,
|
|
182
192
|
spark_model: spark_model.ok_or_else(|| format!("missing --model-spark\n{}", usage()))?,
|
|
183
193
|
fallback_model: fallback_model
|
|
184
194
|
.ok_or_else(|| format!("missing --model-fallback\n{}", usage()))?,
|
|
@@ -198,7 +208,7 @@ where
|
|
|
198
208
|
}
|
|
199
209
|
|
|
200
210
|
fn usage() -> &'static str {
|
|
201
|
-
"Usage: omx-explore --cwd <dir> --prompt <text> --prompt-file <explore-prompt.md> --model-spark <model> --model-fallback <model>"
|
|
211
|
+
"Usage: omx-explore --cwd <dir> --prompt <text> --prompt-file <explore-prompt.md> --instructions-file <AGENTS.md> --model-spark <model> --model-fallback <model>"
|
|
202
212
|
}
|
|
203
213
|
|
|
204
214
|
fn invoke_codex(args: &Args, model: &str, prompt_contract: &str) -> io::Result<AttemptResult> {
|
|
@@ -220,6 +230,11 @@ fn invoke_codex(args: &Args, model: &str, prompt_contract: &str) -> io::Result<A
|
|
|
220
230
|
.arg("-c")
|
|
221
231
|
.arg("model_reasoning_effort=\"low\"")
|
|
222
232
|
.arg("-c")
|
|
233
|
+
.arg(format!(
|
|
234
|
+
"model_instructions_file=\"{}\"",
|
|
235
|
+
escape_toml_string(&args.instructions_file.display().to_string())
|
|
236
|
+
))
|
|
237
|
+
.arg("-c")
|
|
223
238
|
.arg("shell_environment_policy.inherit=all")
|
|
224
239
|
.arg("--skip-git-repo-check")
|
|
225
240
|
.arg("-o")
|
|
@@ -240,6 +255,10 @@ fn invoke_codex(args: &Args, model: &str, prompt_contract: &str) -> io::Result<A
|
|
|
240
255
|
})
|
|
241
256
|
}
|
|
242
257
|
|
|
258
|
+
fn escape_toml_string(value: &str) -> String {
|
|
259
|
+
value.replace('\\', "\\\\").replace('"', "\\\"")
|
|
260
|
+
}
|
|
261
|
+
|
|
243
262
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
244
263
|
struct CodexLaunch {
|
|
245
264
|
program: String,
|
|
@@ -982,6 +1001,8 @@ mod tests {
|
|
|
982
1001
|
"find auth",
|
|
983
1002
|
"--prompt-file",
|
|
984
1003
|
"/tmp/explore.md",
|
|
1004
|
+
"--instructions-file",
|
|
1005
|
+
"/tmp/explore-agents.md",
|
|
985
1006
|
"--model-spark",
|
|
986
1007
|
"gpt-5.3-codex-spark",
|
|
987
1008
|
"--model-fallback",
|
|
@@ -995,6 +1016,7 @@ mod tests {
|
|
|
995
1016
|
assert_eq!(args.cwd, Path::new("/tmp/repo"));
|
|
996
1017
|
assert_eq!(args.prompt, "find auth");
|
|
997
1018
|
assert_eq!(args.prompt_file, Path::new("/tmp/explore.md"));
|
|
1019
|
+
assert_eq!(args.instructions_file, Path::new("/tmp/explore-agents.md"));
|
|
998
1020
|
assert_eq!(args.spark_model, "gpt-5.3-codex-spark");
|
|
999
1021
|
assert_eq!(args.fallback_model, "gpt-5.4");
|
|
1000
1022
|
}
|
|
@@ -1618,6 +1640,8 @@ exit 17
|
|
|
1618
1640
|
"find tests",
|
|
1619
1641
|
"--prompt-file",
|
|
1620
1642
|
prompt_file.to_str().expect("prompt path"),
|
|
1643
|
+
"--instructions-file",
|
|
1644
|
+
prompt_file.to_str().expect("instructions path"),
|
|
1621
1645
|
"--model-spark",
|
|
1622
1646
|
"spark-model",
|
|
1623
1647
|
"--model-fallback",
|
|
@@ -1675,6 +1699,7 @@ printf '# Answer\nok\n' > "$output_path"
|
|
|
1675
1699
|
cwd: repo.clone(),
|
|
1676
1700
|
prompt: "find tests".to_string(),
|
|
1677
1701
|
prompt_file,
|
|
1702
|
+
instructions_file: repo.join("AGENTS.md"),
|
|
1678
1703
|
spark_model: "spark-model".to_string(),
|
|
1679
1704
|
fallback_model: "fallback-model".to_string(),
|
|
1680
1705
|
},
|
|
@@ -1696,6 +1721,74 @@ printf '# Answer\nok\n' > "$output_path"
|
|
|
1696
1721
|
);
|
|
1697
1722
|
}
|
|
1698
1723
|
|
|
1724
|
+
#[test]
|
|
1725
|
+
fn invoke_codex_injects_model_instructions_file_override() {
|
|
1726
|
+
let _guard = env_lock();
|
|
1727
|
+
let root = temp_allowlist_dir().expect("temp root");
|
|
1728
|
+
let repo = root.path.join("repo");
|
|
1729
|
+
create_dir_all(&repo).expect("create repo");
|
|
1730
|
+
let prompt_file = root.path.join("prompt.md");
|
|
1731
|
+
write(&prompt_file, "contract").expect("write prompt");
|
|
1732
|
+
let capture_path = root.path.join("argv.txt");
|
|
1733
|
+
let fake_codex = root.path.join("codex-stub");
|
|
1734
|
+
write_executable(
|
|
1735
|
+
&fake_codex,
|
|
1736
|
+
&format!(
|
|
1737
|
+
r#"#!/bin/sh
|
|
1738
|
+
set -eu
|
|
1739
|
+
output_path=""
|
|
1740
|
+
capture={}
|
|
1741
|
+
printf '' > "$capture"
|
|
1742
|
+
while [ "$#" -gt 0 ]; do
|
|
1743
|
+
printf '%s\n' "$1" >> "$capture"
|
|
1744
|
+
if [ "$1" = "-o" ]; then
|
|
1745
|
+
shift
|
|
1746
|
+
output_path="$1"
|
|
1747
|
+
fi
|
|
1748
|
+
shift
|
|
1749
|
+
done
|
|
1750
|
+
printf '# Answer\nok\n' > "$output_path"
|
|
1751
|
+
"#,
|
|
1752
|
+
shell_quote(&capture_path.display().to_string())
|
|
1753
|
+
),
|
|
1754
|
+
)
|
|
1755
|
+
.expect("write fake codex");
|
|
1756
|
+
|
|
1757
|
+
unsafe {
|
|
1758
|
+
env::set_var(CODEX_BIN_ENV, &fake_codex);
|
|
1759
|
+
}
|
|
1760
|
+
let instructions_path = repo.join("custom instructions.md");
|
|
1761
|
+
let attempt = invoke_codex(
|
|
1762
|
+
&Args {
|
|
1763
|
+
cwd: repo.clone(),
|
|
1764
|
+
prompt: "find tests".to_string(),
|
|
1765
|
+
prompt_file,
|
|
1766
|
+
instructions_file: instructions_path.clone(),
|
|
1767
|
+
spark_model: "spark-model".to_string(),
|
|
1768
|
+
fallback_model: "fallback-model".to_string(),
|
|
1769
|
+
},
|
|
1770
|
+
"spark-model",
|
|
1771
|
+
"contract",
|
|
1772
|
+
)
|
|
1773
|
+
.expect("invoke codex");
|
|
1774
|
+
unsafe {
|
|
1775
|
+
env::remove_var(CODEX_BIN_ENV);
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
assert_eq!(attempt.status_code, 0);
|
|
1779
|
+
let captured = read_to_string(&capture_path).expect("read capture");
|
|
1780
|
+
let expected = format!(
|
|
1781
|
+
"model_instructions_file=\"{}\"",
|
|
1782
|
+
escape_toml_string(&instructions_path.display().to_string())
|
|
1783
|
+
);
|
|
1784
|
+
assert!(
|
|
1785
|
+
captured.contains(&expected),
|
|
1786
|
+
"expected {:?} in {:?}",
|
|
1787
|
+
expected,
|
|
1788
|
+
captured
|
|
1789
|
+
);
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1699
1792
|
#[test]
|
|
1700
1793
|
fn sanitize_explore_subprocess_env_blocks_bash_env_startup_hooks() {
|
|
1701
1794
|
let _guard = env_lock();
|
|
@@ -9,7 +9,7 @@ use std::time::{Duration, Instant};
|
|
|
9
9
|
|
|
10
10
|
pub const DEFAULT_SUMMARY_TIMEOUT_MS: u64 = 60_000;
|
|
11
11
|
pub const DEFAULT_SPARK_MODEL: &str = "gpt-5.3-codex-spark";
|
|
12
|
-
pub const
|
|
12
|
+
pub const DEFAULT_STANDARD_MODEL: &str = "gpt-5.4-mini";
|
|
13
13
|
|
|
14
14
|
pub fn resolve_model() -> String {
|
|
15
15
|
env::var("OMX_SPARKSHELL_MODEL")
|
|
@@ -33,11 +33,18 @@ pub fn resolve_fallback_model() -> String {
|
|
|
33
33
|
.ok()
|
|
34
34
|
.filter(|value| !value.trim().is_empty())
|
|
35
35
|
.or_else(|| {
|
|
36
|
-
env::var("
|
|
36
|
+
env::var("OMX_DEFAULT_STANDARD_MODEL")
|
|
37
37
|
.ok()
|
|
38
38
|
.filter(|value| !value.trim().is_empty())
|
|
39
39
|
})
|
|
40
|
-
.unwrap_or_else(||
|
|
40
|
+
.unwrap_or_else(|| DEFAULT_STANDARD_MODEL.to_string())
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
pub fn resolve_instructions_file() -> Option<String> {
|
|
44
|
+
env::var("OMX_SPARKSHELL_MODEL_INSTRUCTIONS_FILE")
|
|
45
|
+
.ok()
|
|
46
|
+
.map(|value| value.trim().to_string())
|
|
47
|
+
.filter(|value| !value.is_empty())
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
pub fn read_summary_timeout_ms() -> u64 {
|
|
@@ -125,6 +132,12 @@ fn run_codex_exec(
|
|
|
125
132
|
.arg("read-only")
|
|
126
133
|
.arg("-c")
|
|
127
134
|
.arg("model_reasoning_effort=\"low\"")
|
|
135
|
+
.args(resolve_instructions_file().into_iter().flat_map(|path| {
|
|
136
|
+
[
|
|
137
|
+
"-c".to_string(),
|
|
138
|
+
format!("model_instructions_file=\"{}\"", escape_toml_string(&path)),
|
|
139
|
+
]
|
|
140
|
+
}))
|
|
128
141
|
.arg("--skip-git-repo-check")
|
|
129
142
|
.arg("--color")
|
|
130
143
|
.arg("never")
|
|
@@ -191,6 +204,10 @@ fn run_codex_exec(
|
|
|
191
204
|
))
|
|
192
205
|
}
|
|
193
206
|
|
|
207
|
+
fn escape_toml_string(value: &str) -> String {
|
|
208
|
+
value.replace('\\', "\\\\").replace('"', "\\\"")
|
|
209
|
+
}
|
|
210
|
+
|
|
194
211
|
fn normalize_summary(raw: &str) -> Option<String> {
|
|
195
212
|
let mut summary = Vec::new();
|
|
196
213
|
let mut failures = Vec::new();
|
|
@@ -271,8 +288,9 @@ fn render_section(name: &str, entries: &[String]) -> String {
|
|
|
271
288
|
#[cfg(test)]
|
|
272
289
|
mod tests {
|
|
273
290
|
use super::{
|
|
274
|
-
normalize_summary, read_summary_timeout_ms, resolve_fallback_model,
|
|
275
|
-
|
|
291
|
+
normalize_summary, read_summary_timeout_ms, resolve_fallback_model,
|
|
292
|
+
resolve_instructions_file, resolve_model, DEFAULT_SPARK_MODEL, DEFAULT_STANDARD_MODEL,
|
|
293
|
+
DEFAULT_SUMMARY_TIMEOUT_MS,
|
|
276
294
|
};
|
|
277
295
|
use crate::test_support::env_lock;
|
|
278
296
|
use std::env;
|
|
@@ -293,23 +311,23 @@ mod tests {
|
|
|
293
311
|
}
|
|
294
312
|
|
|
295
313
|
#[test]
|
|
296
|
-
fn
|
|
314
|
+
fn fallback_model_resolution_prefers_override_then_default_standard() {
|
|
297
315
|
let _guard = env_lock();
|
|
298
316
|
unsafe {
|
|
299
317
|
env::remove_var("OMX_SPARKSHELL_FALLBACK_MODEL");
|
|
300
|
-
env::remove_var("
|
|
318
|
+
env::remove_var("OMX_DEFAULT_STANDARD_MODEL");
|
|
301
319
|
}
|
|
302
|
-
assert_eq!(resolve_fallback_model(),
|
|
320
|
+
assert_eq!(resolve_fallback_model(), DEFAULT_STANDARD_MODEL);
|
|
303
321
|
|
|
304
322
|
unsafe {
|
|
305
|
-
env::set_var("
|
|
323
|
+
env::set_var("OMX_DEFAULT_STANDARD_MODEL", "standard-a");
|
|
306
324
|
}
|
|
307
|
-
assert_eq!(resolve_fallback_model(), "
|
|
325
|
+
assert_eq!(resolve_fallback_model(), "standard-a");
|
|
308
326
|
|
|
309
327
|
unsafe {
|
|
310
|
-
env::set_var("OMX_SPARKSHELL_FALLBACK_MODEL", "
|
|
328
|
+
env::set_var("OMX_SPARKSHELL_FALLBACK_MODEL", "standard-b");
|
|
311
329
|
}
|
|
312
|
-
assert_eq!(resolve_fallback_model(), "
|
|
330
|
+
assert_eq!(resolve_fallback_model(), "standard-b");
|
|
313
331
|
}
|
|
314
332
|
|
|
315
333
|
#[test]
|
|
@@ -347,6 +365,35 @@ mod tests {
|
|
|
347
365
|
}
|
|
348
366
|
}
|
|
349
367
|
|
|
368
|
+
#[test]
|
|
369
|
+
fn instructions_file_resolution_prefers_override_and_ignores_blank() {
|
|
370
|
+
let _guard = env_lock();
|
|
371
|
+
unsafe {
|
|
372
|
+
env::remove_var("OMX_SPARKSHELL_MODEL_INSTRUCTIONS_FILE");
|
|
373
|
+
}
|
|
374
|
+
assert_eq!(resolve_instructions_file(), None);
|
|
375
|
+
|
|
376
|
+
unsafe {
|
|
377
|
+
env::set_var(
|
|
378
|
+
"OMX_SPARKSHELL_MODEL_INSTRUCTIONS_FILE",
|
|
379
|
+
" /tmp/sparkshell-agents.md ",
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
assert_eq!(
|
|
383
|
+
resolve_instructions_file(),
|
|
384
|
+
Some("/tmp/sparkshell-agents.md".to_string())
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
unsafe {
|
|
388
|
+
env::set_var("OMX_SPARKSHELL_MODEL_INSTRUCTIONS_FILE", " ");
|
|
389
|
+
}
|
|
390
|
+
assert_eq!(resolve_instructions_file(), None);
|
|
391
|
+
|
|
392
|
+
unsafe {
|
|
393
|
+
env::remove_var("OMX_SPARKSHELL_MODEL_INSTRUCTIONS_FILE");
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
350
397
|
#[test]
|
|
351
398
|
fn timeout_defaults_for_zero_and_invalid_values() {
|
|
352
399
|
let _guard = env_lock();
|
|
@@ -97,6 +97,54 @@ fn summary_mode_uses_codex_exec_and_model_override() {
|
|
|
97
97
|
let _ = fs::remove_dir_all(temp);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
#[test]
|
|
101
|
+
fn summary_mode_injects_model_instructions_file_override() {
|
|
102
|
+
let temp = unique_temp_dir("codex-instructions-file");
|
|
103
|
+
let codex = temp.join("codex");
|
|
104
|
+
let args_log = temp.join("args.log");
|
|
105
|
+
let instructions_file = temp.join("sparkshell-lightweight-AGENTS.md");
|
|
106
|
+
write_executable(
|
|
107
|
+
&codex,
|
|
108
|
+
&format!(
|
|
109
|
+
"#!/bin/sh\nprintf '%s\n' \"$@\" > '{}'\nprintf '%s\n' '- summary: command produced long output'\n",
|
|
110
|
+
args_log.display()
|
|
111
|
+
),
|
|
112
|
+
);
|
|
113
|
+
fs::write(&instructions_file, "# sparkshell instructions\n").expect("write instructions file");
|
|
114
|
+
|
|
115
|
+
let path = format!(
|
|
116
|
+
"{}:{}",
|
|
117
|
+
temp.display(),
|
|
118
|
+
env::var("PATH").unwrap_or_default()
|
|
119
|
+
);
|
|
120
|
+
let output = Command::new(sparkshell_bin())
|
|
121
|
+
.env("PATH", path)
|
|
122
|
+
.env("OMX_SPARKSHELL_LINES", "1")
|
|
123
|
+
.env(
|
|
124
|
+
"OMX_SPARKSHELL_MODEL_INSTRUCTIONS_FILE",
|
|
125
|
+
instructions_file.display().to_string(),
|
|
126
|
+
)
|
|
127
|
+
.arg("sh")
|
|
128
|
+
.arg("-c")
|
|
129
|
+
.arg("printf 'one\ntwo\n'")
|
|
130
|
+
.output()
|
|
131
|
+
.expect("run sparkshell");
|
|
132
|
+
|
|
133
|
+
assert!(output.status.success());
|
|
134
|
+
let args = fs::read_to_string(args_log).expect("args log");
|
|
135
|
+
assert!(args.contains("model_reasoning_effort=\"low\""));
|
|
136
|
+
assert!(args.contains(&format!(
|
|
137
|
+
"model_instructions_file=\"{}\"",
|
|
138
|
+
instructions_file
|
|
139
|
+
.display()
|
|
140
|
+
.to_string()
|
|
141
|
+
.replace('\\', "\\\\")
|
|
142
|
+
.replace('"', "\\\"")
|
|
143
|
+
)));
|
|
144
|
+
|
|
145
|
+
let _ = fs::remove_dir_all(temp);
|
|
146
|
+
}
|
|
147
|
+
|
|
100
148
|
#[test]
|
|
101
149
|
fn summary_failure_falls_back_to_raw_output_with_notice() {
|
|
102
150
|
let temp = unique_temp_dir("codex-fail");
|
|
@@ -578,12 +578,43 @@ describe('buildExploreHarnessArgs', () => {
|
|
|
578
578
|
assert.deepEqual(args.slice(4), [
|
|
579
579
|
'--prompt-file',
|
|
580
580
|
'/pkg/prompts/explore-harness.md',
|
|
581
|
+
'--instructions-file',
|
|
582
|
+
'/pkg/templates/model-instructions/explore-lightweight-AGENTS.md',
|
|
581
583
|
'--model-spark',
|
|
582
584
|
'spark-model',
|
|
583
585
|
'--model-fallback',
|
|
584
|
-
'gpt-5.4',
|
|
586
|
+
'gpt-5.4-mini',
|
|
585
587
|
]);
|
|
586
588
|
});
|
|
589
|
+
it('honors configured env overrides for fallback model and instructions file', async () => {
|
|
590
|
+
const codexHome = await mkdtemp(join(tmpdir(), 'omx-explore-config-env-'));
|
|
591
|
+
await writeFile(join(codexHome, '.omx-config.json'), JSON.stringify({
|
|
592
|
+
env: {
|
|
593
|
+
OMX_DEFAULT_STANDARD_MODEL: 'standard-local',
|
|
594
|
+
OMX_DEFAULT_SPARK_MODEL: 'spark-local',
|
|
595
|
+
OMX_EXPLORE_MODEL_INSTRUCTIONS_FILE: '/config/explore-instructions.md',
|
|
596
|
+
},
|
|
597
|
+
}));
|
|
598
|
+
try {
|
|
599
|
+
const wd = join(tmpdir(), 'omx-explore-arg-test');
|
|
600
|
+
const args = buildExploreHarnessArgs('find auth', wd, {
|
|
601
|
+
CODEX_HOME: codexHome,
|
|
602
|
+
}, '/pkg');
|
|
603
|
+
assert.deepEqual(args.slice(4), [
|
|
604
|
+
'--prompt-file',
|
|
605
|
+
'/pkg/prompts/explore-harness.md',
|
|
606
|
+
'--instructions-file',
|
|
607
|
+
'/config/explore-instructions.md',
|
|
608
|
+
'--model-spark',
|
|
609
|
+
'spark-local',
|
|
610
|
+
'--model-fallback',
|
|
611
|
+
'standard-local',
|
|
612
|
+
]);
|
|
613
|
+
}
|
|
614
|
+
finally {
|
|
615
|
+
await rm(codexHome, { recursive: true, force: true });
|
|
616
|
+
}
|
|
617
|
+
});
|
|
587
618
|
});
|
|
588
619
|
describe('resolveExploreSparkShellRoute', () => {
|
|
589
620
|
it('keeps natural-language exploration prompts on the direct harness path', () => {
|
|
@@ -764,6 +795,7 @@ describe('exploreCommand', () => {
|
|
|
764
795
|
assert.match(captured, /ALLOWED_STATUS=0/);
|
|
765
796
|
assert.match(captured, /BLOCKED_STATUS=(?!0)\d+/);
|
|
766
797
|
assert.match(captured, /--ARGV--[\s\S]*\nexec\n/);
|
|
798
|
+
assert.match(captured, /model_instructions_file=.*explore-lightweight-AGENTS\.md/);
|
|
767
799
|
assert.match(captured, /--ALLOWED_STDOUT--[\s\S]*ripgrep/i);
|
|
768
800
|
assert.match(captured, /--BLOCKED_STDERR--[\s\S]*not on the omx explore allowlist/);
|
|
769
801
|
});
|