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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-merge-issue-close-workflow.test.js","sourceRoot":"","sources":["../../../src/verification/__tests__/dev-merge-issue-close-workflow.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EACJ,2BAA2B,EAC3B,wBAAwB,EACxB,8BAA8B,
|
|
1
|
+
{"version":3,"file":"dev-merge-issue-close-workflow.test.js","sourceRoot":"","sources":["../../../src/verification/__tests__/dev-merge-issue-close-workflow.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1464,6 +1464,156 @@ describe("codex native hook dispatch", () => {
|
|
|
1464
1464
|
}
|
|
1465
1465
|
});
|
|
1466
1466
|
|
|
1467
|
+
it("suppresses child-agent SessionStart and Stop before the canonical leader session is reconciled (#2831)", async () => {
|
|
1468
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-no-canonical-"));
|
|
1469
|
+
const originalCodexHome = process.env.CODEX_HOME;
|
|
1470
|
+
try {
|
|
1471
|
+
process.env.CODEX_HOME = join(cwd, "codex-home");
|
|
1472
|
+
await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), {
|
|
1473
|
+
notifications: {
|
|
1474
|
+
enabled: true,
|
|
1475
|
+
verbosity: "session",
|
|
1476
|
+
telegram: { enabled: true, botToken: "123:abc", chatId: "456" },
|
|
1477
|
+
},
|
|
1478
|
+
});
|
|
1479
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1480
|
+
const leaderNativeSessionId = "codex-leader-thread-no-canonical";
|
|
1481
|
+
const childNativeSessionId = "codex-child-thread-no-canonical";
|
|
1482
|
+
await mkdir(join(cwd, ".omx", "hooks"), { recursive: true });
|
|
1483
|
+
await writeFile(
|
|
1484
|
+
join(cwd, ".omx", "hooks", "record-lifecycle.mjs"),
|
|
1485
|
+
[
|
|
1486
|
+
"import { appendFileSync } from 'node:fs';",
|
|
1487
|
+
"export async function onHookEvent(event) {",
|
|
1488
|
+
" appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);",
|
|
1489
|
+
"}",
|
|
1490
|
+
].join("\n"),
|
|
1491
|
+
);
|
|
1492
|
+
const transcriptPath = join(cwd, "no-canonical-subagent-rollout.jsonl");
|
|
1493
|
+
await writeFile(
|
|
1494
|
+
transcriptPath,
|
|
1495
|
+
`${JSON.stringify({
|
|
1496
|
+
type: "session_meta",
|
|
1497
|
+
payload: {
|
|
1498
|
+
id: childNativeSessionId,
|
|
1499
|
+
source: {
|
|
1500
|
+
subagent: {
|
|
1501
|
+
thread_spawn: {
|
|
1502
|
+
parent_thread_id: leaderNativeSessionId,
|
|
1503
|
+
agent_role: "explorer",
|
|
1504
|
+
},
|
|
1505
|
+
},
|
|
1506
|
+
},
|
|
1507
|
+
},
|
|
1508
|
+
})}\n`,
|
|
1509
|
+
);
|
|
1510
|
+
|
|
1511
|
+
await dispatchCodexNativeHook(
|
|
1512
|
+
{
|
|
1513
|
+
hook_event_name: "SessionStart",
|
|
1514
|
+
cwd,
|
|
1515
|
+
session_id: childNativeSessionId,
|
|
1516
|
+
transcript_path: transcriptPath,
|
|
1517
|
+
},
|
|
1518
|
+
{ cwd, sessionOwnerPid: process.pid },
|
|
1519
|
+
);
|
|
1520
|
+
|
|
1521
|
+
assert.equal(
|
|
1522
|
+
existsSync(join(cwd, "hook-events.jsonl")),
|
|
1523
|
+
false,
|
|
1524
|
+
"child SessionStart must be suppressed even before the canonical leader session is reconciled",
|
|
1525
|
+
);
|
|
1526
|
+
assert.equal(
|
|
1527
|
+
existsSync(join(stateDir, "session.json")),
|
|
1528
|
+
false,
|
|
1529
|
+
"child SessionStart must not be promoted into a root/leader session",
|
|
1530
|
+
);
|
|
1531
|
+
|
|
1532
|
+
const tracking = JSON.parse(
|
|
1533
|
+
await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"),
|
|
1534
|
+
) as {
|
|
1535
|
+
sessions?: Record<string, {
|
|
1536
|
+
leader_thread_id?: string;
|
|
1537
|
+
threads?: Record<string, { kind?: string; mode?: string }>;
|
|
1538
|
+
}>;
|
|
1539
|
+
};
|
|
1540
|
+
assert.equal(tracking.sessions?.[leaderNativeSessionId]?.leader_thread_id, leaderNativeSessionId);
|
|
1541
|
+
assert.equal(tracking.sessions?.[leaderNativeSessionId]?.threads?.[childNativeSessionId]?.kind, "subagent");
|
|
1542
|
+
|
|
1543
|
+
await dispatchCodexNativeHook(
|
|
1544
|
+
{
|
|
1545
|
+
hook_event_name: "Stop",
|
|
1546
|
+
cwd,
|
|
1547
|
+
session_id: childNativeSessionId,
|
|
1548
|
+
thread_id: childNativeSessionId,
|
|
1549
|
+
turn_id: "no-canonical-child-stop-turn",
|
|
1550
|
+
},
|
|
1551
|
+
{ cwd },
|
|
1552
|
+
);
|
|
1553
|
+
|
|
1554
|
+
assert.equal(
|
|
1555
|
+
existsSync(join(cwd, "hook-events.jsonl")),
|
|
1556
|
+
false,
|
|
1557
|
+
"child Stop must be suppressed when the start was recognized as subagent-scoped",
|
|
1558
|
+
);
|
|
1559
|
+
} finally {
|
|
1560
|
+
if (originalCodexHome === undefined) {
|
|
1561
|
+
delete process.env.CODEX_HOME;
|
|
1562
|
+
} else {
|
|
1563
|
+
process.env.CODEX_HOME = originalCodexHome;
|
|
1564
|
+
}
|
|
1565
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1566
|
+
}
|
|
1567
|
+
});
|
|
1568
|
+
|
|
1569
|
+
it("preserves root/leader SessionStart dispatch at session verbosity (#2831)", async () => {
|
|
1570
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-session-start-preserved-"));
|
|
1571
|
+
const originalCodexHome = process.env.CODEX_HOME;
|
|
1572
|
+
try {
|
|
1573
|
+
process.env.CODEX_HOME = join(cwd, "codex-home");
|
|
1574
|
+
await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), {
|
|
1575
|
+
notifications: {
|
|
1576
|
+
enabled: true,
|
|
1577
|
+
verbosity: "session",
|
|
1578
|
+
telegram: { enabled: true, botToken: "123:abc", chatId: "456" },
|
|
1579
|
+
},
|
|
1580
|
+
});
|
|
1581
|
+
await mkdir(join(cwd, ".omx", "hooks"), { recursive: true });
|
|
1582
|
+
await writeFile(
|
|
1583
|
+
join(cwd, ".omx", "hooks", "record-lifecycle.mjs"),
|
|
1584
|
+
[
|
|
1585
|
+
"import { appendFileSync } from 'node:fs';",
|
|
1586
|
+
"export async function onHookEvent(event) {",
|
|
1587
|
+
" appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);",
|
|
1588
|
+
"}",
|
|
1589
|
+
].join("\n"),
|
|
1590
|
+
);
|
|
1591
|
+
|
|
1592
|
+
await dispatchCodexNativeHook(
|
|
1593
|
+
{
|
|
1594
|
+
hook_event_name: "SessionStart",
|
|
1595
|
+
cwd,
|
|
1596
|
+
session_id: "codex-root-thread-preserved",
|
|
1597
|
+
},
|
|
1598
|
+
{ cwd, sessionOwnerPid: process.pid },
|
|
1599
|
+
);
|
|
1600
|
+
|
|
1601
|
+
const hookEvents = await readFile(join(cwd, "hook-events.jsonl"), "utf-8");
|
|
1602
|
+
assert.match(
|
|
1603
|
+
hookEvents,
|
|
1604
|
+
/"event":"session-start"/,
|
|
1605
|
+
"root/leader SessionStart must still dispatch at session verbosity",
|
|
1606
|
+
);
|
|
1607
|
+
} finally {
|
|
1608
|
+
if (originalCodexHome === undefined) {
|
|
1609
|
+
delete process.env.CODEX_HOME;
|
|
1610
|
+
} else {
|
|
1611
|
+
process.env.CODEX_HOME = originalCodexHome;
|
|
1612
|
+
}
|
|
1613
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1614
|
+
}
|
|
1615
|
+
});
|
|
1616
|
+
|
|
1467
1617
|
it("keeps a self-parented native role thread as subagent evidence", async () => {
|
|
1468
1618
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-self-parented-subagent-"));
|
|
1469
1619
|
try {
|
|
@@ -5903,6 +6053,219 @@ exit 0
|
|
|
5903
6053
|
}
|
|
5904
6054
|
});
|
|
5905
6055
|
|
|
6056
|
+
it("allows read-only diagnostics mentioning apply_patch while deep-interview blocks real apply_patch invocations", async () => {
|
|
6057
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-grep-apply-patch-"));
|
|
6058
|
+
try {
|
|
6059
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
6060
|
+
const sessionDir = join(stateDir, "sessions", "sess-di-grep");
|
|
6061
|
+
await mkdir(sessionDir, { recursive: true });
|
|
6062
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-di-grep", cwd });
|
|
6063
|
+
await writeJson(join(sessionDir, "skill-active-state.json"), {
|
|
6064
|
+
version: 1,
|
|
6065
|
+
active: true,
|
|
6066
|
+
skill: "deep-interview",
|
|
6067
|
+
phase: "planning",
|
|
6068
|
+
session_id: "sess-di-grep",
|
|
6069
|
+
active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-grep" }],
|
|
6070
|
+
});
|
|
6071
|
+
await writeJson(join(sessionDir, "deep-interview-state.json"), {
|
|
6072
|
+
active: true,
|
|
6073
|
+
mode: "deep-interview",
|
|
6074
|
+
current_phase: "intent-first",
|
|
6075
|
+
session_id: "sess-di-grep",
|
|
6076
|
+
});
|
|
6077
|
+
|
|
6078
|
+
const allowedGrep = await dispatchCodexNativeHook(
|
|
6079
|
+
{
|
|
6080
|
+
hook_event_name: "PreToolUse",
|
|
6081
|
+
cwd,
|
|
6082
|
+
session_id: "sess-di-grep",
|
|
6083
|
+
tool_name: "Bash",
|
|
6084
|
+
tool_use_id: "tool-di-grep",
|
|
6085
|
+
tool_input: { command: 'grep -n "apply_patch" dist/scripts/codex-native-hook.js' },
|
|
6086
|
+
},
|
|
6087
|
+
{ cwd },
|
|
6088
|
+
);
|
|
6089
|
+
assert.equal(allowedGrep.outputJson, null);
|
|
6090
|
+
|
|
6091
|
+
const allowedSubshellGrep = await dispatchCodexNativeHook(
|
|
6092
|
+
{
|
|
6093
|
+
hook_event_name: "PreToolUse",
|
|
6094
|
+
cwd,
|
|
6095
|
+
session_id: "sess-di-grep",
|
|
6096
|
+
tool_name: "Bash",
|
|
6097
|
+
tool_use_id: "tool-di-grep-subshell",
|
|
6098
|
+
tool_input: { command: '(grep -n "apply_patch" dist/scripts/codex-native-hook.js)' },
|
|
6099
|
+
},
|
|
6100
|
+
{ cwd },
|
|
6101
|
+
);
|
|
6102
|
+
assert.equal(allowedSubshellGrep.outputJson, null);
|
|
6103
|
+
|
|
6104
|
+
// Double-quoted spans that merely mention the literal token, expand a
|
|
6105
|
+
// parameter, or run a substitution that is not `apply_patch` stay allowed
|
|
6106
|
+
// — the quoted-substitution fix must not over-block read-only diagnostics.
|
|
6107
|
+
const allowedQuotedMention = await dispatchCodexNativeHook(
|
|
6108
|
+
{
|
|
6109
|
+
hook_event_name: "PreToolUse",
|
|
6110
|
+
cwd,
|
|
6111
|
+
session_id: "sess-di-grep",
|
|
6112
|
+
tool_name: "Bash",
|
|
6113
|
+
tool_use_id: "tool-di-grep-quoted-mention",
|
|
6114
|
+
tool_input: { command: 'echo "${apply_patch} $(echo apply_patch)"' },
|
|
6115
|
+
},
|
|
6116
|
+
{ cwd },
|
|
6117
|
+
);
|
|
6118
|
+
assert.equal(allowedQuotedMention.outputJson, null);
|
|
6119
|
+
|
|
6120
|
+
const blockedApplyPatch = await dispatchCodexNativeHook(
|
|
6121
|
+
{
|
|
6122
|
+
hook_event_name: "PreToolUse",
|
|
6123
|
+
cwd,
|
|
6124
|
+
session_id: "sess-di-grep",
|
|
6125
|
+
tool_name: "Bash",
|
|
6126
|
+
tool_use_id: "tool-di-apply-patch-invoke",
|
|
6127
|
+
tool_input: { command: "apply_patch <<'EOF'\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF" },
|
|
6128
|
+
},
|
|
6129
|
+
{ cwd },
|
|
6130
|
+
);
|
|
6131
|
+
assert.equal((blockedApplyPatch.outputJson as { decision?: string } | null)?.decision, "block");
|
|
6132
|
+
|
|
6133
|
+
const blockedEnvAssignmentApplyPatch = await dispatchCodexNativeHook(
|
|
6134
|
+
{
|
|
6135
|
+
hook_event_name: "PreToolUse",
|
|
6136
|
+
cwd,
|
|
6137
|
+
session_id: "sess-di-grep",
|
|
6138
|
+
tool_name: "Bash",
|
|
6139
|
+
tool_use_id: "tool-di-apply-patch-env-assignment",
|
|
6140
|
+
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" },
|
|
6141
|
+
},
|
|
6142
|
+
{ cwd },
|
|
6143
|
+
);
|
|
6144
|
+
assert.equal((blockedEnvAssignmentApplyPatch.outputJson as { decision?: string } | null)?.decision, "block");
|
|
6145
|
+
|
|
6146
|
+
const blockedEnvWrapperApplyPatch = await dispatchCodexNativeHook(
|
|
6147
|
+
{
|
|
6148
|
+
hook_event_name: "PreToolUse",
|
|
6149
|
+
cwd,
|
|
6150
|
+
session_id: "sess-di-grep",
|
|
6151
|
+
tool_name: "Bash",
|
|
6152
|
+
tool_use_id: "tool-di-apply-patch-env-wrapper",
|
|
6153
|
+
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" },
|
|
6154
|
+
},
|
|
6155
|
+
{ cwd },
|
|
6156
|
+
);
|
|
6157
|
+
assert.equal((blockedEnvWrapperApplyPatch.outputJson as { decision?: string } | null)?.decision, "block");
|
|
6158
|
+
|
|
6159
|
+
const heredocBody = "\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF";
|
|
6160
|
+
const blockedRealApplyPatchForms: Array<{ id: string; command: string }> = [
|
|
6161
|
+
{ id: "tool-di-apply-patch-env-i", command: `env -i apply_patch <<'EOF'${heredocBody}` },
|
|
6162
|
+
{ id: "tool-di-apply-patch-env-unset", command: `env -u FOO apply_patch <<'EOF'${heredocBody}` },
|
|
6163
|
+
{ id: "tool-di-apply-patch-env-i-assignment", command: `env -i FOO=bar apply_patch <<'EOF'${heredocBody}` },
|
|
6164
|
+
{ id: "tool-di-apply-patch-assignment-env", command: `FOO=bar env apply_patch <<'EOF'${heredocBody}` },
|
|
6165
|
+
{ id: "tool-di-apply-patch-exec-env-assignment", command: `exec env FOO=bar apply_patch <<'EOF'${heredocBody}` },
|
|
6166
|
+
{ id: "tool-di-apply-patch-absolute-path", command: `/usr/bin/apply_patch <<'EOF'${heredocBody}` },
|
|
6167
|
+
{ id: "tool-di-apply-patch-relative-path", command: `./apply_patch <<'EOF'${heredocBody}` },
|
|
6168
|
+
{ id: "tool-di-apply-patch-subshell", command: `(apply_patch <<'EOF'${heredocBody}\n)` },
|
|
6169
|
+
{ id: "tool-di-apply-patch-subshell-spaced", command: `( apply_patch <<'EOF'${heredocBody}\n)` },
|
|
6170
|
+
{ id: "tool-di-apply-patch-double-subshell", command: `((apply_patch <<'EOF'${heredocBody}\n))` },
|
|
6171
|
+
{ id: "tool-di-apply-patch-pipe-subshell", command: `true | (apply_patch <<'EOF'${heredocBody}\n)` },
|
|
6172
|
+
{ id: "tool-di-apply-patch-subshell-env", command: `(env apply_patch <<'EOF'${heredocBody}\n)` },
|
|
6173
|
+
{ id: "tool-di-apply-patch-command-substitution", command: `x=$(apply_patch <<'EOF'${heredocBody}\n)` },
|
|
6174
|
+
{ id: "tool-di-apply-patch-brace-group", command: `{ apply_patch <<'EOF'${heredocBody}\n}` },
|
|
6175
|
+
// Command substitution runs even inside double quotes, so quoting the
|
|
6176
|
+
// already-blocked `$(…)` / `` `…` `` form must not bypass the guard.
|
|
6177
|
+
{ id: "tool-di-apply-patch-quoted-command-substitution", command: `echo "$(apply_patch <<'EOF'${heredocBody}\n)"` },
|
|
6178
|
+
{ id: "tool-di-apply-patch-quoted-backtick", command: `echo "\`apply_patch <<'EOF'${heredocBody}\`"` },
|
|
6179
|
+
{ id: "tool-di-apply-patch-quoted-command-substitution-prefixed", command: `echo "patched: $(apply_patch <<'EOF'${heredocBody}\n) done"` },
|
|
6180
|
+
];
|
|
6181
|
+
for (const form of blockedRealApplyPatchForms) {
|
|
6182
|
+
const blockedForm = await dispatchCodexNativeHook(
|
|
6183
|
+
{
|
|
6184
|
+
hook_event_name: "PreToolUse",
|
|
6185
|
+
cwd,
|
|
6186
|
+
session_id: "sess-di-grep",
|
|
6187
|
+
tool_name: "Bash",
|
|
6188
|
+
tool_use_id: form.id,
|
|
6189
|
+
tool_input: { command: form.command },
|
|
6190
|
+
},
|
|
6191
|
+
{ cwd },
|
|
6192
|
+
);
|
|
6193
|
+
assert.equal(
|
|
6194
|
+
(blockedForm.outputJson as { decision?: string } | null)?.decision,
|
|
6195
|
+
"block",
|
|
6196
|
+
`expected deep-interview to block real apply_patch form: ${form.command}`,
|
|
6197
|
+
);
|
|
6198
|
+
}
|
|
6199
|
+
} finally {
|
|
6200
|
+
await rm(cwd, { recursive: true, force: true });
|
|
6201
|
+
}
|
|
6202
|
+
});
|
|
6203
|
+
|
|
6204
|
+
it("allows deep-interview same-command literal variable redirects to artifacts while blocking variable redirects outside them", async () => {
|
|
6205
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-var-redirect-"));
|
|
6206
|
+
try {
|
|
6207
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
6208
|
+
const sessionDir = join(stateDir, "sessions", "sess-di-var-redirect");
|
|
6209
|
+
await mkdir(sessionDir, { recursive: true });
|
|
6210
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-di-var-redirect", cwd });
|
|
6211
|
+
await writeJson(join(sessionDir, "skill-active-state.json"), {
|
|
6212
|
+
version: 1,
|
|
6213
|
+
active: true,
|
|
6214
|
+
skill: "deep-interview",
|
|
6215
|
+
phase: "planning",
|
|
6216
|
+
session_id: "sess-di-var-redirect",
|
|
6217
|
+
active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-var-redirect" }],
|
|
6218
|
+
});
|
|
6219
|
+
await writeJson(join(sessionDir, "deep-interview-state.json"), {
|
|
6220
|
+
active: true,
|
|
6221
|
+
mode: "deep-interview",
|
|
6222
|
+
current_phase: "intent-first",
|
|
6223
|
+
session_id: "sess-di-var-redirect",
|
|
6224
|
+
});
|
|
6225
|
+
|
|
6226
|
+
const allowedVarRedirect = await dispatchCodexNativeHook(
|
|
6227
|
+
{
|
|
6228
|
+
hook_event_name: "PreToolUse",
|
|
6229
|
+
cwd,
|
|
6230
|
+
session_id: "sess-di-var-redirect",
|
|
6231
|
+
tool_name: "Bash",
|
|
6232
|
+
tool_use_id: "tool-di-var-redirect-allow",
|
|
6233
|
+
tool_input: { command: 'SNAP=".omx/context/example.md"\ncat > "$SNAP" <<\'EOF\'\ncontent\nEOF' },
|
|
6234
|
+
},
|
|
6235
|
+
{ cwd },
|
|
6236
|
+
);
|
|
6237
|
+
assert.equal(allowedVarRedirect.outputJson, null);
|
|
6238
|
+
|
|
6239
|
+
const blockedVarRedirect = await dispatchCodexNativeHook(
|
|
6240
|
+
{
|
|
6241
|
+
hook_event_name: "PreToolUse",
|
|
6242
|
+
cwd,
|
|
6243
|
+
session_id: "sess-di-var-redirect",
|
|
6244
|
+
tool_name: "Bash",
|
|
6245
|
+
tool_use_id: "tool-di-var-redirect-block",
|
|
6246
|
+
tool_input: { command: 'SNAP="src/leak.ts"\ncat > "$SNAP" <<\'EOF\'\ncontent\nEOF' },
|
|
6247
|
+
},
|
|
6248
|
+
{ cwd },
|
|
6249
|
+
);
|
|
6250
|
+
assert.equal((blockedVarRedirect.outputJson as { decision?: string } | null)?.decision, "block");
|
|
6251
|
+
|
|
6252
|
+
const blockedUnresolvedVarRedirect = await dispatchCodexNativeHook(
|
|
6253
|
+
{
|
|
6254
|
+
hook_event_name: "PreToolUse",
|
|
6255
|
+
cwd,
|
|
6256
|
+
session_id: "sess-di-var-redirect",
|
|
6257
|
+
tool_name: "Bash",
|
|
6258
|
+
tool_use_id: "tool-di-var-redirect-unresolved",
|
|
6259
|
+
tool_input: { command: 'cat > "$SNAP" <<\'EOF\'\ncontent\nEOF' },
|
|
6260
|
+
},
|
|
6261
|
+
{ cwd },
|
|
6262
|
+
);
|
|
6263
|
+
assert.equal((blockedUnresolvedVarRedirect.outputJson as { decision?: string } | null)?.decision, "block");
|
|
6264
|
+
} finally {
|
|
6265
|
+
await rm(cwd, { recursive: true, force: true });
|
|
6266
|
+
}
|
|
6267
|
+
});
|
|
6268
|
+
|
|
5906
6269
|
it("allows deep-interview apply_patch artifact writes from freeform patch text while blocking outside paths", async () => {
|
|
5907
6270
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-apply-patch-"));
|
|
5908
6271
|
try {
|
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
SLOPPY_FALLBACK_PHRASE_PATTERNS,
|
|
62
62
|
buildNativePostToolUseOutput,
|
|
63
63
|
buildNativePreToolUseOutput,
|
|
64
|
+
commandInvokesApplyPatch,
|
|
64
65
|
detectMcpTransportFailure,
|
|
65
66
|
hasAnyPattern,
|
|
66
67
|
} from "./codex-native-pre-post.js";
|
|
@@ -2751,6 +2752,31 @@ function isNullDeviceRedirectTarget(target: string): boolean {
|
|
|
2751
2752
|
return normalized === "/dev/null" || normalized === "nul";
|
|
2752
2753
|
}
|
|
2753
2754
|
|
|
2755
|
+
// Collects same-command literal variable assignments (`NAME="value"`), skipping
|
|
2756
|
+
// any value that involves expansion (`$`, backticks) so unresolved/dynamic
|
|
2757
|
+
// targets stay conservatively blocked.
|
|
2758
|
+
function extractCommandLiteralAssignments(command: string): Map<string, string> {
|
|
2759
|
+
const assignments = new Map<string, string>();
|
|
2760
|
+
const pattern = /(?:^|[\n;&|(]|&&|\|\|)\s*([A-Za-z_][A-Za-z0-9_]*)=(?:"([^"$`]*)"|'([^']*)'|([^\s"'$`;&|<>]+))/g;
|
|
2761
|
+
for (const match of command.matchAll(pattern)) {
|
|
2762
|
+
const name = safeString(match[1]).trim();
|
|
2763
|
+
if (!name) continue;
|
|
2764
|
+
const value = match[2] ?? match[3] ?? match[4] ?? "";
|
|
2765
|
+
assignments.set(name, value);
|
|
2766
|
+
}
|
|
2767
|
+
return assignments;
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
// Resolves a redirect/tee target of the form `$NAME`/`${NAME}` against
|
|
2771
|
+
// same-command literal assignments; non-variable or unresolved targets are
|
|
2772
|
+
// returned unchanged so they remain subject to the allowed-path check.
|
|
2773
|
+
function resolveCommandRedirectTarget(target: string, assignments: Map<string, string>): string {
|
|
2774
|
+
const variableMatch = target.match(/^\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?$/);
|
|
2775
|
+
if (!variableMatch) return target;
|
|
2776
|
+
const resolved = assignments.get(safeString(variableMatch[1]));
|
|
2777
|
+
return resolved !== undefined ? resolved : target;
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2754
2780
|
function extractDeepInterviewCommandRedirectTargets(command: string): string[] {
|
|
2755
2781
|
const targets: string[] = [];
|
|
2756
2782
|
for (const match of command.matchAll(/(?:^|[^>])>{1,2}\s*(["']?)([^\s&|;<>]+)\1/g)) {
|
|
@@ -2761,7 +2787,7 @@ function extractDeepInterviewCommandRedirectTargets(command: string): string[] {
|
|
|
2761
2787
|
}
|
|
2762
2788
|
|
|
2763
2789
|
function commandHasDeepInterviewWriteIntent(command: string): boolean {
|
|
2764
|
-
return
|
|
2790
|
+
return commandInvokesApplyPatch(command)
|
|
2765
2791
|
|| extractDeepInterviewCommandRedirectTargets(command).length > 0
|
|
2766
2792
|
|| /\btee\s+(?:-a\s+)?[^\s&|;]+/.test(command)
|
|
2767
2793
|
|| /\bsed\s+(?:[^\n;&|]*\s)?-i(?:\b|['"])/.test(command)
|
|
@@ -2770,10 +2796,12 @@ function commandHasDeepInterviewWriteIntent(command: string): boolean {
|
|
|
2770
2796
|
}
|
|
2771
2797
|
|
|
2772
2798
|
function extractDeepInterviewCommandWriteTargets(command: string): string[] {
|
|
2773
|
-
const
|
|
2799
|
+
const assignments = extractCommandLiteralAssignments(command);
|
|
2800
|
+
const targets = extractDeepInterviewCommandRedirectTargets(command)
|
|
2801
|
+
.map((target) => resolveCommandRedirectTarget(target, assignments));
|
|
2774
2802
|
for (const match of command.matchAll(/\btee\s+(?:-a\s+)?(["']?)([^\s&|;<>]+)\1/g)) {
|
|
2775
2803
|
const candidate = safeString(match[2]).trim();
|
|
2776
|
-
if (candidate) targets.push(candidate);
|
|
2804
|
+
if (candidate) targets.push(resolveCommandRedirectTarget(candidate, assignments));
|
|
2777
2805
|
}
|
|
2778
2806
|
return targets;
|
|
2779
2807
|
}
|
|
@@ -4221,28 +4249,50 @@ export async function dispatchCodexNativeHook(
|
|
|
4221
4249
|
if (hookEventName === "SessionStart" && nativeSessionId) {
|
|
4222
4250
|
const transcriptPath = safeString(payload.transcript_path ?? payload.transcriptPath).trim();
|
|
4223
4251
|
const subagentSessionStart = readNativeSubagentSessionStartMetadata(transcriptPath);
|
|
4224
|
-
if (subagentSessionStart
|
|
4252
|
+
if (subagentSessionStart) {
|
|
4253
|
+
// A native child/subagent SessionStart carries a parent_thread_id in its
|
|
4254
|
+
// transcript session_meta. Treat it as a child-agent lifecycle event for
|
|
4255
|
+
// notification suppression and subagent tracking even when the canonical
|
|
4256
|
+
// leader session has not been reconciled yet (#2831). A child start must
|
|
4257
|
+
// never promote itself into a root/leader session or emit an independent
|
|
4258
|
+
// session-start notification at session/minimal verbosity.
|
|
4225
4259
|
isSubagentSessionStart = true;
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
canonicalSessionId,
|
|
4229
|
-
currentSessionState,
|
|
4230
|
-
subagentSessionStart,
|
|
4231
|
-
);
|
|
4232
|
-
if (belongsToCanonicalSession) {
|
|
4233
|
-
resolvedNativeSessionId = nativeSessionId;
|
|
4234
|
-
await recordNativeSubagentSessionStart(
|
|
4260
|
+
if (canonicalSessionId) {
|
|
4261
|
+
const belongsToCanonicalSession = await nativeSubagentSessionStartBelongsToCanonicalSession(
|
|
4235
4262
|
cwd,
|
|
4236
4263
|
canonicalSessionId,
|
|
4237
|
-
|
|
4264
|
+
currentSessionState,
|
|
4238
4265
|
subagentSessionStart,
|
|
4239
|
-
transcriptPath,
|
|
4240
4266
|
);
|
|
4267
|
+
if (belongsToCanonicalSession) {
|
|
4268
|
+
resolvedNativeSessionId = nativeSessionId;
|
|
4269
|
+
await recordNativeSubagentSessionStart(
|
|
4270
|
+
cwd,
|
|
4271
|
+
canonicalSessionId,
|
|
4272
|
+
nativeSessionId,
|
|
4273
|
+
subagentSessionStart,
|
|
4274
|
+
transcriptPath,
|
|
4275
|
+
);
|
|
4276
|
+
} else {
|
|
4277
|
+
skipCanonicalSessionStartContext = true;
|
|
4278
|
+
resolvedNativeSessionId =
|
|
4279
|
+
safeString(currentSessionState?.native_session_id).trim() || nativeSessionId;
|
|
4280
|
+
await recordIgnoredNativeSubagentSessionStart(
|
|
4281
|
+
cwd,
|
|
4282
|
+
canonicalSessionId,
|
|
4283
|
+
nativeSessionId,
|
|
4284
|
+
subagentSessionStart,
|
|
4285
|
+
transcriptPath,
|
|
4286
|
+
);
|
|
4287
|
+
}
|
|
4241
4288
|
} else {
|
|
4289
|
+
// No canonical leader session is resolved in this worktree yet. Still
|
|
4290
|
+
// register the child thread under its parent so its later Stop is
|
|
4291
|
+
// recognized as subagent-scoped, skip leader SessionStart context, and
|
|
4292
|
+
// do not reconcile the child as a new root session.
|
|
4242
4293
|
skipCanonicalSessionStartContext = true;
|
|
4243
|
-
resolvedNativeSessionId =
|
|
4244
|
-
|
|
4245
|
-
await recordIgnoredNativeSubagentSessionStart(
|
|
4294
|
+
resolvedNativeSessionId = nativeSessionId;
|
|
4295
|
+
await recordNativeSubagentSessionStart(
|
|
4246
4296
|
cwd,
|
|
4247
4297
|
canonicalSessionId,
|
|
4248
4298
|
nativeSessionId,
|