oh-my-codex 0.18.12 → 0.18.13
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 +6 -6
- package/Cargo.toml +1 -1
- package/README.md +7 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js +621 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
- package/dist/autopilot/ralplan-gate.js +32 -18
- package/dist/autopilot/ralplan-gate.js.map +1 -1
- package/dist/cli/__tests__/doctor-invalid-config.test.js +35 -0
- package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +54 -1
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/resume.test.js +217 -1
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/session-search-help.test.js +3 -2
- package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
- package/dist/cli/__tests__/session-search.test.js +64 -2
- package/dist/cli/__tests__/session-search.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +6 -5
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js +74 -0
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +45 -0
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +5 -2
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +9 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +9 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +209 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/project-runtime-codex-homes.d.ts +6 -0
- package/dist/cli/project-runtime-codex-homes.d.ts.map +1 -0
- package/dist/cli/project-runtime-codex-homes.js +27 -0
- package/dist/cli/project-runtime-codex-homes.js.map +1 -0
- package/dist/cli/session-search.d.ts.map +1 -1
- package/dist/cli/session-search.js +8 -1
- package/dist/cli/session-search.js.map +1 -1
- package/dist/cli/setup.d.ts +1 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +168 -4
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +2 -0
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/codex-hooks.test.js +38 -24
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts +6 -0
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +34 -49
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/pipeline/__tests__/orchestrator.test.js +125 -0
- package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +109 -0
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/pipeline/orchestrator.js +7 -0
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/ralplan/__tests__/consensus-gate.test.js +440 -1
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts +2 -0
- package/dist/ralplan/consensus-gate.d.ts.map +1 -1
- package/dist/ralplan/consensus-gate.js +173 -71
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +273 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +56 -12
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts +1 -0
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +130 -0
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/session-history/__tests__/search.test.js +166 -0
- package/dist/session-history/__tests__/search.test.js.map +1 -1
- package/dist/session-history/search.d.ts +7 -0
- package/dist/session-history/search.d.ts.map +1 -1
- package/dist/session-history/search.js +83 -24
- package/dist/session-history/search.js.map +1 -1
- package/dist/sidecar/__tests__/collector.test.js +60 -0
- package/dist/sidecar/__tests__/collector.test.js.map +1 -1
- package/dist/sidecar/collector.d.ts.map +1 -1
- package/dist/sidecar/collector.js +3 -6
- package/dist/sidecar/collector.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +4 -2
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +71 -3
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/src/scripts/__tests__/codex-native-hook.test.ts +363 -0
- package/src/scripts/codex-native-hook.ts +68 -18
- package/src/scripts/codex-native-pre-post.ts +137 -0
|
@@ -1153,6 +1153,110 @@ describe("codex native hook dispatch", () => {
|
|
|
1153
1153
|
await rm(cwd, { recursive: true, force: true });
|
|
1154
1154
|
}
|
|
1155
1155
|
});
|
|
1156
|
+
it("suppresses child-agent SessionStart and Stop before the canonical leader session is reconciled (#2831)", async () => {
|
|
1157
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-no-canonical-"));
|
|
1158
|
+
const originalCodexHome = process.env.CODEX_HOME;
|
|
1159
|
+
try {
|
|
1160
|
+
process.env.CODEX_HOME = join(cwd, "codex-home");
|
|
1161
|
+
await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), {
|
|
1162
|
+
notifications: {
|
|
1163
|
+
enabled: true,
|
|
1164
|
+
verbosity: "session",
|
|
1165
|
+
telegram: { enabled: true, botToken: "123:abc", chatId: "456" },
|
|
1166
|
+
},
|
|
1167
|
+
});
|
|
1168
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1169
|
+
const leaderNativeSessionId = "codex-leader-thread-no-canonical";
|
|
1170
|
+
const childNativeSessionId = "codex-child-thread-no-canonical";
|
|
1171
|
+
await mkdir(join(cwd, ".omx", "hooks"), { recursive: true });
|
|
1172
|
+
await writeFile(join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [
|
|
1173
|
+
"import { appendFileSync } from 'node:fs';",
|
|
1174
|
+
"export async function onHookEvent(event) {",
|
|
1175
|
+
" appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);",
|
|
1176
|
+
"}",
|
|
1177
|
+
].join("\n"));
|
|
1178
|
+
const transcriptPath = join(cwd, "no-canonical-subagent-rollout.jsonl");
|
|
1179
|
+
await writeFile(transcriptPath, `${JSON.stringify({
|
|
1180
|
+
type: "session_meta",
|
|
1181
|
+
payload: {
|
|
1182
|
+
id: childNativeSessionId,
|
|
1183
|
+
source: {
|
|
1184
|
+
subagent: {
|
|
1185
|
+
thread_spawn: {
|
|
1186
|
+
parent_thread_id: leaderNativeSessionId,
|
|
1187
|
+
agent_role: "explorer",
|
|
1188
|
+
},
|
|
1189
|
+
},
|
|
1190
|
+
},
|
|
1191
|
+
},
|
|
1192
|
+
})}\n`);
|
|
1193
|
+
await dispatchCodexNativeHook({
|
|
1194
|
+
hook_event_name: "SessionStart",
|
|
1195
|
+
cwd,
|
|
1196
|
+
session_id: childNativeSessionId,
|
|
1197
|
+
transcript_path: transcriptPath,
|
|
1198
|
+
}, { cwd, sessionOwnerPid: process.pid });
|
|
1199
|
+
assert.equal(existsSync(join(cwd, "hook-events.jsonl")), false, "child SessionStart must be suppressed even before the canonical leader session is reconciled");
|
|
1200
|
+
assert.equal(existsSync(join(stateDir, "session.json")), false, "child SessionStart must not be promoted into a root/leader session");
|
|
1201
|
+
const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"));
|
|
1202
|
+
assert.equal(tracking.sessions?.[leaderNativeSessionId]?.leader_thread_id, leaderNativeSessionId);
|
|
1203
|
+
assert.equal(tracking.sessions?.[leaderNativeSessionId]?.threads?.[childNativeSessionId]?.kind, "subagent");
|
|
1204
|
+
await dispatchCodexNativeHook({
|
|
1205
|
+
hook_event_name: "Stop",
|
|
1206
|
+
cwd,
|
|
1207
|
+
session_id: childNativeSessionId,
|
|
1208
|
+
thread_id: childNativeSessionId,
|
|
1209
|
+
turn_id: "no-canonical-child-stop-turn",
|
|
1210
|
+
}, { cwd });
|
|
1211
|
+
assert.equal(existsSync(join(cwd, "hook-events.jsonl")), false, "child Stop must be suppressed when the start was recognized as subagent-scoped");
|
|
1212
|
+
}
|
|
1213
|
+
finally {
|
|
1214
|
+
if (originalCodexHome === undefined) {
|
|
1215
|
+
delete process.env.CODEX_HOME;
|
|
1216
|
+
}
|
|
1217
|
+
else {
|
|
1218
|
+
process.env.CODEX_HOME = originalCodexHome;
|
|
1219
|
+
}
|
|
1220
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1223
|
+
it("preserves root/leader SessionStart dispatch at session verbosity (#2831)", async () => {
|
|
1224
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-session-start-preserved-"));
|
|
1225
|
+
const originalCodexHome = process.env.CODEX_HOME;
|
|
1226
|
+
try {
|
|
1227
|
+
process.env.CODEX_HOME = join(cwd, "codex-home");
|
|
1228
|
+
await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), {
|
|
1229
|
+
notifications: {
|
|
1230
|
+
enabled: true,
|
|
1231
|
+
verbosity: "session",
|
|
1232
|
+
telegram: { enabled: true, botToken: "123:abc", chatId: "456" },
|
|
1233
|
+
},
|
|
1234
|
+
});
|
|
1235
|
+
await mkdir(join(cwd, ".omx", "hooks"), { recursive: true });
|
|
1236
|
+
await writeFile(join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [
|
|
1237
|
+
"import { appendFileSync } from 'node:fs';",
|
|
1238
|
+
"export async function onHookEvent(event) {",
|
|
1239
|
+
" appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);",
|
|
1240
|
+
"}",
|
|
1241
|
+
].join("\n"));
|
|
1242
|
+
await dispatchCodexNativeHook({
|
|
1243
|
+
hook_event_name: "SessionStart",
|
|
1244
|
+
cwd,
|
|
1245
|
+
session_id: "codex-root-thread-preserved",
|
|
1246
|
+
}, { cwd, sessionOwnerPid: process.pid });
|
|
1247
|
+
const hookEvents = await readFile(join(cwd, "hook-events.jsonl"), "utf-8");
|
|
1248
|
+
assert.match(hookEvents, /"event":"session-start"/, "root/leader SessionStart must still dispatch at session verbosity");
|
|
1249
|
+
}
|
|
1250
|
+
finally {
|
|
1251
|
+
if (originalCodexHome === undefined) {
|
|
1252
|
+
delete process.env.CODEX_HOME;
|
|
1253
|
+
}
|
|
1254
|
+
else {
|
|
1255
|
+
process.env.CODEX_HOME = originalCodexHome;
|
|
1256
|
+
}
|
|
1257
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1156
1260
|
it("keeps a self-parented native role thread as subagent evidence", async () => {
|
|
1157
1261
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-self-parented-subagent-"));
|
|
1158
1262
|
try {
|
|
@@ -4915,6 +5019,175 @@ exit 0
|
|
|
4915
5019
|
await rm(cwd, { recursive: true, force: true });
|
|
4916
5020
|
}
|
|
4917
5021
|
});
|
|
5022
|
+
it("allows read-only diagnostics mentioning apply_patch while deep-interview blocks real apply_patch invocations", async () => {
|
|
5023
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-grep-apply-patch-"));
|
|
5024
|
+
try {
|
|
5025
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
5026
|
+
const sessionDir = join(stateDir, "sessions", "sess-di-grep");
|
|
5027
|
+
await mkdir(sessionDir, { recursive: true });
|
|
5028
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-di-grep", cwd });
|
|
5029
|
+
await writeJson(join(sessionDir, "skill-active-state.json"), {
|
|
5030
|
+
version: 1,
|
|
5031
|
+
active: true,
|
|
5032
|
+
skill: "deep-interview",
|
|
5033
|
+
phase: "planning",
|
|
5034
|
+
session_id: "sess-di-grep",
|
|
5035
|
+
active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-grep" }],
|
|
5036
|
+
});
|
|
5037
|
+
await writeJson(join(sessionDir, "deep-interview-state.json"), {
|
|
5038
|
+
active: true,
|
|
5039
|
+
mode: "deep-interview",
|
|
5040
|
+
current_phase: "intent-first",
|
|
5041
|
+
session_id: "sess-di-grep",
|
|
5042
|
+
});
|
|
5043
|
+
const allowedGrep = await dispatchCodexNativeHook({
|
|
5044
|
+
hook_event_name: "PreToolUse",
|
|
5045
|
+
cwd,
|
|
5046
|
+
session_id: "sess-di-grep",
|
|
5047
|
+
tool_name: "Bash",
|
|
5048
|
+
tool_use_id: "tool-di-grep",
|
|
5049
|
+
tool_input: { command: 'grep -n "apply_patch" dist/scripts/codex-native-hook.js' },
|
|
5050
|
+
}, { cwd });
|
|
5051
|
+
assert.equal(allowedGrep.outputJson, null);
|
|
5052
|
+
const allowedSubshellGrep = await dispatchCodexNativeHook({
|
|
5053
|
+
hook_event_name: "PreToolUse",
|
|
5054
|
+
cwd,
|
|
5055
|
+
session_id: "sess-di-grep",
|
|
5056
|
+
tool_name: "Bash",
|
|
5057
|
+
tool_use_id: "tool-di-grep-subshell",
|
|
5058
|
+
tool_input: { command: '(grep -n "apply_patch" dist/scripts/codex-native-hook.js)' },
|
|
5059
|
+
}, { cwd });
|
|
5060
|
+
assert.equal(allowedSubshellGrep.outputJson, null);
|
|
5061
|
+
// Double-quoted spans that merely mention the literal token, expand a
|
|
5062
|
+
// parameter, or run a substitution that is not `apply_patch` stay allowed
|
|
5063
|
+
// — the quoted-substitution fix must not over-block read-only diagnostics.
|
|
5064
|
+
const allowedQuotedMention = await dispatchCodexNativeHook({
|
|
5065
|
+
hook_event_name: "PreToolUse",
|
|
5066
|
+
cwd,
|
|
5067
|
+
session_id: "sess-di-grep",
|
|
5068
|
+
tool_name: "Bash",
|
|
5069
|
+
tool_use_id: "tool-di-grep-quoted-mention",
|
|
5070
|
+
tool_input: { command: 'echo "${apply_patch} $(echo apply_patch)"' },
|
|
5071
|
+
}, { cwd });
|
|
5072
|
+
assert.equal(allowedQuotedMention.outputJson, null);
|
|
5073
|
+
const blockedApplyPatch = await dispatchCodexNativeHook({
|
|
5074
|
+
hook_event_name: "PreToolUse",
|
|
5075
|
+
cwd,
|
|
5076
|
+
session_id: "sess-di-grep",
|
|
5077
|
+
tool_name: "Bash",
|
|
5078
|
+
tool_use_id: "tool-di-apply-patch-invoke",
|
|
5079
|
+
tool_input: { command: "apply_patch <<'EOF'\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF" },
|
|
5080
|
+
}, { cwd });
|
|
5081
|
+
assert.equal(blockedApplyPatch.outputJson?.decision, "block");
|
|
5082
|
+
const blockedEnvAssignmentApplyPatch = await dispatchCodexNativeHook({
|
|
5083
|
+
hook_event_name: "PreToolUse",
|
|
5084
|
+
cwd,
|
|
5085
|
+
session_id: "sess-di-grep",
|
|
5086
|
+
tool_name: "Bash",
|
|
5087
|
+
tool_use_id: "tool-di-apply-patch-env-assignment",
|
|
5088
|
+
tool_input: { command: "FOO=bar apply_patch <<'EOF'\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF" },
|
|
5089
|
+
}, { cwd });
|
|
5090
|
+
assert.equal(blockedEnvAssignmentApplyPatch.outputJson?.decision, "block");
|
|
5091
|
+
const blockedEnvWrapperApplyPatch = await dispatchCodexNativeHook({
|
|
5092
|
+
hook_event_name: "PreToolUse",
|
|
5093
|
+
cwd,
|
|
5094
|
+
session_id: "sess-di-grep",
|
|
5095
|
+
tool_name: "Bash",
|
|
5096
|
+
tool_use_id: "tool-di-apply-patch-env-wrapper",
|
|
5097
|
+
tool_input: { command: "env FOO=bar apply_patch <<'EOF'\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF" },
|
|
5098
|
+
}, { cwd });
|
|
5099
|
+
assert.equal(blockedEnvWrapperApplyPatch.outputJson?.decision, "block");
|
|
5100
|
+
const heredocBody = "\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF";
|
|
5101
|
+
const blockedRealApplyPatchForms = [
|
|
5102
|
+
{ id: "tool-di-apply-patch-env-i", command: `env -i apply_patch <<'EOF'${heredocBody}` },
|
|
5103
|
+
{ id: "tool-di-apply-patch-env-unset", command: `env -u FOO apply_patch <<'EOF'${heredocBody}` },
|
|
5104
|
+
{ id: "tool-di-apply-patch-env-i-assignment", command: `env -i FOO=bar apply_patch <<'EOF'${heredocBody}` },
|
|
5105
|
+
{ id: "tool-di-apply-patch-assignment-env", command: `FOO=bar env apply_patch <<'EOF'${heredocBody}` },
|
|
5106
|
+
{ id: "tool-di-apply-patch-exec-env-assignment", command: `exec env FOO=bar apply_patch <<'EOF'${heredocBody}` },
|
|
5107
|
+
{ id: "tool-di-apply-patch-absolute-path", command: `/usr/bin/apply_patch <<'EOF'${heredocBody}` },
|
|
5108
|
+
{ id: "tool-di-apply-patch-relative-path", command: `./apply_patch <<'EOF'${heredocBody}` },
|
|
5109
|
+
{ id: "tool-di-apply-patch-subshell", command: `(apply_patch <<'EOF'${heredocBody}\n)` },
|
|
5110
|
+
{ id: "tool-di-apply-patch-subshell-spaced", command: `( apply_patch <<'EOF'${heredocBody}\n)` },
|
|
5111
|
+
{ id: "tool-di-apply-patch-double-subshell", command: `((apply_patch <<'EOF'${heredocBody}\n))` },
|
|
5112
|
+
{ id: "tool-di-apply-patch-pipe-subshell", command: `true | (apply_patch <<'EOF'${heredocBody}\n)` },
|
|
5113
|
+
{ id: "tool-di-apply-patch-subshell-env", command: `(env apply_patch <<'EOF'${heredocBody}\n)` },
|
|
5114
|
+
{ id: "tool-di-apply-patch-command-substitution", command: `x=$(apply_patch <<'EOF'${heredocBody}\n)` },
|
|
5115
|
+
{ id: "tool-di-apply-patch-brace-group", command: `{ apply_patch <<'EOF'${heredocBody}\n}` },
|
|
5116
|
+
// Command substitution runs even inside double quotes, so quoting the
|
|
5117
|
+
// already-blocked `$(…)` / `` `…` `` form must not bypass the guard.
|
|
5118
|
+
{ id: "tool-di-apply-patch-quoted-command-substitution", command: `echo "$(apply_patch <<'EOF'${heredocBody}\n)"` },
|
|
5119
|
+
{ id: "tool-di-apply-patch-quoted-backtick", command: `echo "\`apply_patch <<'EOF'${heredocBody}\`"` },
|
|
5120
|
+
{ id: "tool-di-apply-patch-quoted-command-substitution-prefixed", command: `echo "patched: $(apply_patch <<'EOF'${heredocBody}\n) done"` },
|
|
5121
|
+
];
|
|
5122
|
+
for (const form of blockedRealApplyPatchForms) {
|
|
5123
|
+
const blockedForm = await dispatchCodexNativeHook({
|
|
5124
|
+
hook_event_name: "PreToolUse",
|
|
5125
|
+
cwd,
|
|
5126
|
+
session_id: "sess-di-grep",
|
|
5127
|
+
tool_name: "Bash",
|
|
5128
|
+
tool_use_id: form.id,
|
|
5129
|
+
tool_input: { command: form.command },
|
|
5130
|
+
}, { cwd });
|
|
5131
|
+
assert.equal(blockedForm.outputJson?.decision, "block", `expected deep-interview to block real apply_patch form: ${form.command}`);
|
|
5132
|
+
}
|
|
5133
|
+
}
|
|
5134
|
+
finally {
|
|
5135
|
+
await rm(cwd, { recursive: true, force: true });
|
|
5136
|
+
}
|
|
5137
|
+
});
|
|
5138
|
+
it("allows deep-interview same-command literal variable redirects to artifacts while blocking variable redirects outside them", async () => {
|
|
5139
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-var-redirect-"));
|
|
5140
|
+
try {
|
|
5141
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
5142
|
+
const sessionDir = join(stateDir, "sessions", "sess-di-var-redirect");
|
|
5143
|
+
await mkdir(sessionDir, { recursive: true });
|
|
5144
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-di-var-redirect", cwd });
|
|
5145
|
+
await writeJson(join(sessionDir, "skill-active-state.json"), {
|
|
5146
|
+
version: 1,
|
|
5147
|
+
active: true,
|
|
5148
|
+
skill: "deep-interview",
|
|
5149
|
+
phase: "planning",
|
|
5150
|
+
session_id: "sess-di-var-redirect",
|
|
5151
|
+
active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-var-redirect" }],
|
|
5152
|
+
});
|
|
5153
|
+
await writeJson(join(sessionDir, "deep-interview-state.json"), {
|
|
5154
|
+
active: true,
|
|
5155
|
+
mode: "deep-interview",
|
|
5156
|
+
current_phase: "intent-first",
|
|
5157
|
+
session_id: "sess-di-var-redirect",
|
|
5158
|
+
});
|
|
5159
|
+
const allowedVarRedirect = await dispatchCodexNativeHook({
|
|
5160
|
+
hook_event_name: "PreToolUse",
|
|
5161
|
+
cwd,
|
|
5162
|
+
session_id: "sess-di-var-redirect",
|
|
5163
|
+
tool_name: "Bash",
|
|
5164
|
+
tool_use_id: "tool-di-var-redirect-allow",
|
|
5165
|
+
tool_input: { command: 'SNAP=".omx/context/example.md"\ncat > "$SNAP" <<\'EOF\'\ncontent\nEOF' },
|
|
5166
|
+
}, { cwd });
|
|
5167
|
+
assert.equal(allowedVarRedirect.outputJson, null);
|
|
5168
|
+
const blockedVarRedirect = await dispatchCodexNativeHook({
|
|
5169
|
+
hook_event_name: "PreToolUse",
|
|
5170
|
+
cwd,
|
|
5171
|
+
session_id: "sess-di-var-redirect",
|
|
5172
|
+
tool_name: "Bash",
|
|
5173
|
+
tool_use_id: "tool-di-var-redirect-block",
|
|
5174
|
+
tool_input: { command: 'SNAP="src/leak.ts"\ncat > "$SNAP" <<\'EOF\'\ncontent\nEOF' },
|
|
5175
|
+
}, { cwd });
|
|
5176
|
+
assert.equal(blockedVarRedirect.outputJson?.decision, "block");
|
|
5177
|
+
const blockedUnresolvedVarRedirect = await dispatchCodexNativeHook({
|
|
5178
|
+
hook_event_name: "PreToolUse",
|
|
5179
|
+
cwd,
|
|
5180
|
+
session_id: "sess-di-var-redirect",
|
|
5181
|
+
tool_name: "Bash",
|
|
5182
|
+
tool_use_id: "tool-di-var-redirect-unresolved",
|
|
5183
|
+
tool_input: { command: 'cat > "$SNAP" <<\'EOF\'\ncontent\nEOF' },
|
|
5184
|
+
}, { cwd });
|
|
5185
|
+
assert.equal(blockedUnresolvedVarRedirect.outputJson?.decision, "block");
|
|
5186
|
+
}
|
|
5187
|
+
finally {
|
|
5188
|
+
await rm(cwd, { recursive: true, force: true });
|
|
5189
|
+
}
|
|
5190
|
+
});
|
|
4918
5191
|
it("allows deep-interview apply_patch artifact writes from freeform patch text while blocking outside paths", async () => {
|
|
4919
5192
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-apply-patch-"));
|
|
4920
5193
|
try {
|